Como faço download de um arquivo via HTTP usando Python?
Pergunta
Eu tenho um pequeno utilitário que uso para baixar um MP3 de um site de acordo com uma programação e, em seguida, criar/atualizar um arquivo XML de podcast que obviamente adicionei ao iTunes.
O processamento de texto que cria/atualiza o arquivo XML é escrito em Python.Eu uso o wget dentro de um Windows .bat
arquivo para baixar o MP3 real.Eu preferiria ter todo o utilitário escrito em Python.
Eu me esforcei para encontrar uma maneira de realmente baixar o arquivo em Python, por isso recorri a wget
.
Então, como faço o download do arquivo usando Python?
Solução
No Python 2, use urllib2 que vem com a biblioteca padrão.
import urllib2
response = urllib2.urlopen('http://www.example.com/')
html = response.read()
Esta é a maneira mais básica de usar a biblioteca, sem qualquer tratamento de erros.Você também pode fazer coisas mais complexas, como alterar cabeçalhos.A documentação pode ser encontrada aqui.
Outras dicas
Mais um, usando urlretrieve
:
import urllib
urllib.urlretrieve ("http://www.example.com/songs/mp3.mp3", "mp3.mp3")
(para uso em Python 3+ import urllib.request
e urllib.request.urlretrieve
)
Mais um, com uma "barra de progresso"
import urllib2
url = "http://download.thinkbroadband.com/10MB.zip"
file_name = url.split('/')[-1]
u = urllib2.urlopen(url)
f = open(file_name, 'wb')
meta = u.info()
file_size = int(meta.getheaders("Content-Length")[0])
print "Downloading: %s Bytes: %s" % (file_name, file_size)
file_size_dl = 0
block_sz = 8192
while True:
buffer = u.read(block_sz)
if not buffer:
break
file_size_dl += len(buffer)
f.write(buffer)
status = r"%10d [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size)
status = status + chr(8)*(len(status)+1)
print status,
f.close()
Em 2012, use o biblioteca de solicitações python
>>> import requests
>>>
>>> url = "http://download.thinkbroadband.com/10MB.zip"
>>> r = requests.get(url)
>>> print len(r.content)
10485760
Você pode correr pip install requests
para obtê-la.
As solicitações têm muitas vantagens sobre as alternativas porque a API é muito mais simples.Isto é especialmente verdadeiro se você precisar fazer autenticação.urllib e urllib2 são pouco intuitivos e dolorosos neste caso.
2015-12-30
As pessoas expressaram admiração pela barra de progresso.É legal, claro.Existem várias soluções prontas para uso agora, incluindo tqdm
:
from tqdm import tqdm
import requests
url = "http://download.thinkbroadband.com/10MB.zip"
response = requests.get(url, stream=True)
with open("10MB", "wb") as handle:
for data in tqdm(response.iter_content()):
handle.write(data)
Esta é essencialmente a implementação que @kvance descreveu há 30 meses.
import urllib2
mp3file = urllib2.urlopen("http://www.example.com/songs/mp3.mp3")
with open('test.mp3','wb') as output:
output.write(mp3file.read())
O wb
em open('test.mp3','wb')
abre um arquivo (e apaga qualquer arquivo existente) em modo binário para que você possa salvar dados com ele em vez de apenas texto.
Pitão 3
-
import urllib.request response = urllib.request.urlopen('http://www.example.com/') html = response.read()
-
import urllib.request urllib.request.urlretrieve('http://www.example.com/songs/mp3.mp3', 'mp3.mp3')
Pitão 2
urllib2.urlopen
(obrigado Corey)import urllib2 response = urllib2.urlopen('http://www.example.com/') html = response.read()
urllib.urlretrieve
(obrigado PabloG)import urllib urllib.urlretrieve('http://www.example.com/songs/mp3.mp3', 'mp3.mp3')
use o módulo wget:
import wget
wget.download('url')
Uma versão melhorada do código PabloG para Python 2/3:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import ( division, absolute_import, print_function, unicode_literals )
import sys, os, tempfile, logging
if sys.version_info >= (3,):
import urllib.request as urllib2
import urllib.parse as urlparse
else:
import urllib2
import urlparse
def download_file(url, dest=None):
"""
Download and save a file specified by url to dest directory,
"""
u = urllib2.urlopen(url)
scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
filename = os.path.basename(path)
if not filename:
filename = 'downloaded.file'
if dest:
filename = os.path.join(dest, filename)
with open(filename, 'wb') as f:
meta = u.info()
meta_func = meta.getheaders if hasattr(meta, 'getheaders') else meta.get_all
meta_length = meta_func("Content-Length")
file_size = None
if meta_length:
file_size = int(meta_length[0])
print("Downloading: {0} Bytes: {1}".format(url, file_size))
file_size_dl = 0
block_sz = 8192
while True:
buffer = u.read(block_sz)
if not buffer:
break
file_size_dl += len(buffer)
f.write(buffer)
status = "{0:16}".format(file_size_dl)
if file_size:
status += " [{0:6.2f}%]".format(file_size_dl * 100 / file_size)
status += chr(13)
print(status, end="")
print()
return filename
if __name__ == "__main__": # Only run if this file is called directly
print("Testing with 10MB download")
url = "http://download.thinkbroadband.com/10MB.zip"
filename = download_file(url)
print(filename)
Escreveu wget biblioteca em Python puro apenas para esse propósito.Está bombado urlretrieve
com esses recursos a partir da versão 2.0.
Simples ainda Python 2 & Python 3
maneira compatível vem com six
biblioteca:
from six.moves import urllib
urllib.request.urlretrieve("http://www.example.com/songs/mp3.mp3", "mp3.mp3")
Concordo com Corey, urllib2 é mais completo que URLlib e provavelmente deve ser o módulo usado se você quiser fazer coisas mais complexas, mas para tornar as respostas mais completas, urllib é um módulo mais simples se você quiser apenas o básico:
import urllib
response = urllib.urlopen('http://www.example.com/sound.mp3')
mp3 = response.read()
Funcionará bem.Ou, se você não quiser lidar com o objeto "resposta", você pode chamar ler() diretamente:
import urllib
mp3 = urllib.urlopen('http://www.example.com/sound.mp3').read()
A seguir estão as chamadas mais comumente usadas para download de arquivos em python:
urllib.urlretrieve ('url_to_file', file_name)
urllib2.urlopen('url_to_file')
requests.get(url)
wget.download('url', file_name)
Observação: urlopen
e urlretrieve
apresentam desempenho relativamente ruim no download de arquivos grandes (tamanho> 500 MB). requests.get
armazena o arquivo na memória até que o download seja concluído.
import os,requests
def download(url):
get_response = requests.get(url,stream=True)
file_name = url.split("/")[-1]
with open(file_name, 'wb') as f:
for chunk in get_response.iter_content(chunk_size=1024):
if chunk: # filter out keep-alive new chunks
f.write(chunk)
download("https://example.com/example.jpg")
Você também pode obter feedback do progresso com urlretrieve:
def report(blocknr, blocksize, size):
current = blocknr*blocksize
sys.stdout.write("\r{0:.2f}%".format(100.0*current/size))
def downloadFile(url):
print "\n",url
fname = url.split('/')[-1]
print fname
urllib.urlretrieve(url, fname, report)
Se você tiver o wget instalado, poderá usar o paralelo_sync.
pip instalar paralelo_sync
from parallel_sync import wget
urls = ['http://something.png', 'http://somthing.tar.gz', 'http://somthing.zip']
wget.download('/tmp', urls)
# or a single file:
wget.download('/tmp', urls[0], filenames='x.zip', extract=True)
Documento:https://pythonhosted.org/parallel_sync/pages/examples.html
Isso é muito poderoso.Ele pode baixar arquivos em paralelo, tentar novamente em caso de falha e até mesmo baixar arquivos em uma máquina remota.
Em python3 você pode usar bibliotecas urllib3 e shutil.Baixe-os usando pip ou pip3 (dependendo se python3 é padrão ou não)
pip3 install urllib3 shutil
Então execute este código
import urllib.request
import shutil
url = "http://www.somewebsite.com/something.pdf"
output_file = "save_this_name.pdf"
with urllib.request.urlopen(url) as response, open(output_file, 'wb') as out_file:
shutil.copyfileobj(response, out_file)
Observe que você baixa urllib3
mas use urllib
em código
Se velocidade é importante para você, fiz um pequeno teste de desempenho dos módulos urllib
e wget
, e em relação wget
Tentei uma vez com barra de status e outra sem.Peguei três arquivos diferentes de 500 MB para testar (arquivos diferentes - para eliminar a chance de haver algum cache acontecendo nos bastidores).Testado em máquina Debian, com python2.
Primeiro, estes são os resultados (eles são semelhantes em execuções diferentes):
$ python wget_test.py
urlretrive_test : starting
urlretrive_test : 6.56
==============
wget_no_bar_test : starting
wget_no_bar_test : 7.20
==============
wget_with_bar_test : starting
100% [......................................................................] 541335552 / 541335552
wget_with_bar_test : 50.49
==============
A forma como realizei o teste foi usando o decorador "perfil".Este é o código completo:
import wget
import urllib
import time
from functools import wraps
def profile(func):
@wraps(func)
def inner(*args):
print func.__name__, ": starting"
start = time.time()
ret = func(*args)
end = time.time()
print func.__name__, ": {:.2f}".format(end - start)
return ret
return inner
url1 = 'http://host.com/500a.iso'
url2 = 'http://host.com/500b.iso'
url3 = 'http://host.com/500c.iso'
def do_nothing(*args):
pass
@profile
def urlretrive_test(url):
return urllib.urlretrieve(url)
@profile
def wget_no_bar_test(url):
return wget.download(url, out='/tmp/', bar=do_nothing)
@profile
def wget_with_bar_test(url):
return wget.download(url, out='/tmp/')
urlretrive_test(url1)
print '=============='
time.sleep(1)
wget_no_bar_test(url2)
print '=============='
time.sleep(1)
wget_with_bar_test(url3)
print '=============='
time.sleep(1)
urllib
parece ser o mais rápido
Apenas por uma questão de exaustividade, também é possível chamar qualquer programa para recuperação de arquivos usando o subprocess
pacote.Programas dedicados à recuperação de arquivos são mais poderosos que funções Python como urlretrieve
.Por exemplo, wget
pode baixar diretórios recursivamente (-R
), pode lidar com FTP, redirecionamentos, proxies HTTP, pode evitar baixar novamente arquivos existentes (-nc
), e aria2
pode fazer downloads de múltiplas conexões, o que pode potencialmente acelerar seus downloads.
import subprocess
subprocess.check_output(['wget', '-O', 'example_output_file.html', 'https://example.com'])
No Jupyter Notebook, também é possível chamar programas diretamente com o !
sintaxe:
!wget -O example_output_file.html https://example.com
O código-fonte pode ser:
import urllib
sock = urllib.urlopen("http://diveintopython.org/")
htmlSource = sock.read()
sock.close()
print htmlSource
Eu escrevi o seguinte, que funciona no vanilla Python 2 ou Python 3.
import sys
try:
import urllib.request
python3 = True
except ImportError:
import urllib2
python3 = False
def progress_callback_simple(downloaded,total):
sys.stdout.write(
"\r" +
(len(str(total))-len(str(downloaded)))*" " + str(downloaded) + "/%d"%total +
" [%3.2f%%]"%(100.0*float(downloaded)/float(total))
)
sys.stdout.flush()
def download(srcurl, dstfilepath, progress_callback=None, block_size=8192):
def _download_helper(response, out_file, file_size):
if progress_callback!=None: progress_callback(0,file_size)
if block_size == None:
buffer = response.read()
out_file.write(buffer)
if progress_callback!=None: progress_callback(file_size,file_size)
else:
file_size_dl = 0
while True:
buffer = response.read(block_size)
if not buffer: break
file_size_dl += len(buffer)
out_file.write(buffer)
if progress_callback!=None: progress_callback(file_size_dl,file_size)
with open(dstfilepath,"wb") as out_file:
if python3:
with urllib.request.urlopen(srcurl) as response:
file_size = int(response.getheader("Content-Length"))
_download_helper(response,out_file,file_size)
else:
response = urllib2.urlopen(srcurl)
meta = response.info()
file_size = int(meta.getheaders("Content-Length")[0])
_download_helper(response,out_file,file_size)
import traceback
try:
download(
"https://geometrian.com/data/programming/projects/glLib/glLib%20Reloaded%200.5.9/0.5.9.zip",
"output.zip",
progress_callback_simple
)
except:
traceback.print_exc()
input()
Notas:
- Suporta um retorno de chamada de "barra de progresso".
- O download é um .zip de teste de 4 MB do meu site.
Você pode usar PycURL em Python 2 e 3.
import pycurl
FILE_DEST = 'pycurl.html'
FILE_SRC = 'http://pycurl.io/'
with open(FILE_DEST, 'wb') as f:
c = pycurl.Curl()
c.setopt(c.URL, FILE_SRC)
c.setopt(c.WRITEDATA, f)
c.perform()
c.close()
Pode ser um pouco tarde, mas vi o código do pabloG e não pude deixar de adicionar um os.system('cls') para torná-lo INCRÍVEL!Confira :
import urllib2,os
url = "http://download.thinkbroadband.com/10MB.zip"
file_name = url.split('/')[-1]
u = urllib2.urlopen(url)
f = open(file_name, 'wb')
meta = u.info()
file_size = int(meta.getheaders("Content-Length")[0])
print "Downloading: %s Bytes: %s" % (file_name, file_size)
os.system('cls')
file_size_dl = 0
block_sz = 8192
while True:
buffer = u.read(block_sz)
if not buffer:
break
file_size_dl += len(buffer)
f.write(buffer)
status = r"%10d [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size)
status = status + chr(8)*(len(status)+1)
print status,
f.close()
Se estiver executando em um ambiente diferente do Windows, você terá que usar algo diferente de 'cls'.No MAC OS X e Linux deve estar 'claro'.
urlretrieve e requests.get são simples, mas a realidade não.Busquei dados de alguns sites, incluindo texto e imagens. Os dois acima provavelmente resolvem a maioria das tarefas.mas para uma solução mais universal sugiro o uso de urlopen.Como está incluído na biblioteca padrão do Python 3, seu código pode ser executado em qualquer máquina que execute o Python 3 sem pré-instalar o site-package
import urllib.request
url_request = urllib.request.Request(url, headers=headers)
url_connect = urllib.request.urlopen(url_request)
#remember to open file in bytes mode
with open(filename, 'wb') as f:
while True:
buffer = url_connect.read(buffer_size)
if not buffer: break
#an integer value of size of written data
data_wrote = f.write(buffer)
#you could probably use with-open-as manner
url_connect.close()
Esta resposta fornece uma solução para HTTP 403 Proibido ao baixar arquivos por http usando Python.Tentei apenas solicitações e módulos urllib, o outro módulo pode fornecer algo melhor, mas este é o que usei para resolver a maioria dos problemas.