¿Cómo descargo un archivo a través de HTTP usando Python?
Pregunta
Tengo una pequeña utilidad que uso para descargar un MP3 de un sitio web según un cronograma y luego compila/actualiza un archivo XML de podcast que obviamente agregué a iTunes.
El procesamiento de texto que crea/actualiza el archivo XML está escrito en Python.Yo uso wget dentro de Windows .bat
archivo para descargar el MP3 real sin embargo.Sin embargo, preferiría tener toda la utilidad escrita en Python.
Sin embargo, me costó encontrar una manera de descargar el archivo en Python, por eso recurrí a wget
.
Entonces, ¿cómo descargo el archivo usando Python?
Solución
En Python 2, use urllib2 que viene con la biblioteca estándar.
import urllib2
response = urllib2.urlopen('http://www.example.com/')
html = response.read()
Esta es la forma más básica de utilizar la biblioteca, menos el manejo de errores.También puedes hacer cosas más complejas, como cambiar encabezados.La documentación se puede encontrar. aquí.
Otros consejos
Uno más, usando urlretrieve
:
import urllib
urllib.urlretrieve ("http://www.example.com/songs/mp3.mp3", "mp3.mp3")
(para uso de Python 3+ import urllib.request
y urllib.request.urlretrieve
)
Otro más, con una "barra de progreso"
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()
En 2012, utilice el biblioteca de solicitudes de Python
>>> import requests
>>>
>>> url = "http://download.thinkbroadband.com/10MB.zip"
>>> r = requests.get(url)
>>> print len(r.content)
10485760
Tu puedes correr pip install requests
para conseguirlo.
Las solicitudes tienen muchas ventajas sobre las alternativas porque la API es mucho más simple.Esto es especialmente cierto si tiene que realizar una autenticación.urllib y urllib2 son bastante poco intuitivos y dolorosos en este caso.
2015-12-30
La gente ha expresado admiración por la barra de progreso.Es genial, seguro.Actualmente existen varias soluciones disponibles en el mercado, entre ellas 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 es esencialmente la implementación que @kvance describió hace 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())
El wb
en open('test.mp3','wb')
abre un archivo (y borra cualquier archivo existente) en modo binario para que pueda guardar datos con él en lugar de solo texto.
Pitón 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ón 2
urllib2.urlopen
(gracias Corey)import urllib2 response = urllib2.urlopen('http://www.example.com/') html = response.read()
urllib.urlretrieve
(gracias pablog)import urllib urllib.urlretrieve('http://www.example.com/songs/mp3.mp3', 'mp3.mp3')
utilizar el módulo wget:
import wget
wget.download('url')
Una versión mejorada del 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)
Escribió obtener biblioteca en Python puro solo para este propósito.esta inflado urlretrieve
con estas características a partir de la versión 2.0.
Simple todavía Python 2 & Python 3
forma compatible viene con six
biblioteca:
from six.moves import urllib
urllib.request.urlretrieve("http://www.example.com/songs/mp3.mp3", "mp3.mp3")
Estoy de acuerdo con Corey, urllib2 es más completo que URLlib y probablemente debería ser el módulo utilizado si quieres hacer cosas más complejas, pero para que las respuestas sean más completas, urllib es un módulo más simple si solo quieres lo básico:
import urllib
response = urllib.urlopen('http://www.example.com/sound.mp3')
mp3 = response.read()
Funcionará bien.O, si no desea tratar con el objeto de "respuesta", puede llamar leer() directamente:
import urllib
mp3 = urllib.urlopen('http://www.example.com/sound.mp3').read()
Las siguientes son las llamadas más utilizadas para descargar archivos en Python:
urllib.urlretrieve ('url_to_file', file_name)
urllib2.urlopen('url_to_file')
requests.get(url)
wget.download('url', file_name)
Nota: urlopen
y urlretrieve
Se ha descubierto que funcionan relativamente mal al descargar archivos grandes (tamaño > 500 MB). requests.get
almacena el archivo en la memoria hasta que se completa la descarga.
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")
También puede obtener comentarios sobre el progreso con 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)
Si tiene wget instalado, puede usar paralelo_sync.
instalación de pip sincronización_paralela
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)
Doc:https://pythonhosted.org/parallel_sync/pages/examples.html
Esto es bastante poderoso.Puede descargar archivos en paralelo, reintentar en caso de error e incluso puede descargar archivos en una máquina remota.
En python3 puedes usar las bibliotecas urllib3 y Shutil.Descárgalos usando pip o pip3 (dependiendo de si python3 es el predeterminado o no)
pip3 install urllib3 shutil
Luego ejecuta 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)
Tenga en cuenta que descarga urllib3
pero usa urllib
en codigo
Si la velocidad te importa, hice una pequeña prueba de rendimiento de los módulos. urllib
y wget
, y respecto a wget
Lo intenté una vez con la barra de estado y otra sin ella.Tomé tres archivos diferentes de 500 MB para probar (archivos diferentes, para eliminar la posibilidad de que haya algo de almacenamiento en caché bajo el capó).Probado en máquina Debian, con python2.
Primero, estos son los resultados (son similares en diferentes ejecuciones):
$ 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
==============
La forma en que realicé la prueba fue utilizando el decorador de "perfil".Este es el 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 el mas rapido
Para completar, también es posible llamar a cualquier programa para recuperar archivos usando el subprocess
paquete.Los programas dedicados a recuperar archivos son más poderosos que las funciones de Python como urlretrieve
.Por ejemplo, wget
puede descargar directorios de forma recursiva (-R
), puede manejar FTP, redirecciones, servidores proxy HTTP, puede evitar volver a descargar archivos existentes (-nc
), y aria2
Puede realizar descargas con múltiples conexiones, lo que potencialmente puede acelerar sus descargas.
import subprocess
subprocess.check_output(['wget', '-O', 'example_output_file.html', 'https://example.com'])
En Jupyter Notebook, también se pueden llamar programas directamente con el !
sintaxis:
!wget -O example_output_file.html https://example.com
El código fuente puede ser:
import urllib
sock = urllib.urlopen("http://diveintopython.org/")
htmlSource = sock.read()
sock.close()
print htmlSource
Escribí lo siguiente, que funciona en Vanilla Python 2 o 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:
- Admite una devolución de llamada de "barra de progreso".
- La descarga es un archivo .zip de prueba de 4 MB desde mi sitio web.
Puedes usar PycURL en Python 2 y 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()
Puede que esto sea un poco tarde, pero vi el código de pabloG y no pude evitar agregar un os.system('cls') para que se vea IMPRESIONANTE.Échale un vistazo :
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()
Si se ejecuta en un entorno que no sea Windows, tendrá que usar algo que no sea 'cls'.En MAC OS X y Linux debería estar "claro".
urlretrieve y request.get son simples, pero en realidad no.He obtenido datos de un par de sitios, incluidos texto e imágenes, los dos anteriores probablemente resuelvan la mayoría de las tareas.pero para una solución más universal sugiero el uso de urlopen.Como está incluido en la biblioteca estándar de Python 3, su código podría ejecutarse en cualquier máquina que ejecute Python 3 sin preinstalar el paquete del sitio.
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 respuesta proporciona una solución a HTTP 403 prohibido al descargar archivos a través de http usando Python.Solo probé los módulos request y urllib, el otro módulo puede proporcionar algo mejor, pero este es el que usé para resolver la mayoría de los problemas.