¿Cómo descargo un archivo a través de HTTP usando Python?

Tengo una pequeña utilidad que uso para descargar un MP3 de un sitio web en un horario y luego compilo / actualizo un archivo XML de podcast que obviamente he agregado a iTunes.

El procesamiento de texto que crea / actualiza el archivo XML está escrito en Python. Sin embargo, uso wget dentro de un archivo .bat Windows para descargar el MP3 real. Sin embargo, preferiría tener toda la utilidad escrita en Python.

Sin embargo, luché por encontrar una manera de descargar el archivo en Python, por lo que recurrí a wget .

Entonces, ¿cómo descargo el archivo usando Python?

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 usar la biblioteca, menos cualquier manejo de errores. También puedes hacer cosas más complejas como cambiar los encabezados. La documentación se puede encontrar aquí.

Una más, usando urlretrieve :

 import urllib urllib.urlretrieve ("http://www.example.com/songs/mp3.mp3", "mp3.mp3") 

(para Python 3+ use import urllib.request y urllib.request.urlretrieve )

Otra 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, usa la biblioteca de peticiones python.

 >>> import requests >>> >>> url = "http://download.thinkbroadband.com/10MB.zip" >>> r = requests.get(url) >>> print len(r.content) 10485760 

Puede ejecutar pip install requests para obtenerlo.

Las solicitudes tienen muchas ventajas sobre las alternativas porque la API es mucho más simple. Esto es especialmente cierto si tiene que hacer 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. Está bien, claro. Ahora hay varias soluciones disponibles, incluyendo 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 en lugar de solo texto.

Python 3

  • urllib.request.urlopen

     import urllib.request response = urllib.request.urlopen('http://www.example.com/') html = response.read() 
  • urllib.request.urlretrieve

     import urllib.request urllib.request.urlretrieve('http://www.example.com/songs/mp3.mp3', 'mp3.mp3') 

Python 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') 

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) 

usar el modulo wget:

 import wget wget.download('url') 

Escribió wget library en Python puro solo para este propósito. Se bombea urlretrieve con estas características a partir de la versión 2.0.

La forma simple pero compatible con Python 2 & Python 3 viene con six bibliotecas:

 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 quiere hacer cosas más complejas, pero para que las respuestas sean más completas, urllib es un módulo más simple si solo desea lo básico:

 import urllib response = urllib.urlopen('http://www.example.com/sound.mp3') mp3 = response.read() 

Funcionará bien. O, si no desea tratar el objeto de “respuesta”, puede llamar a read () 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:

  1. urllib.urlretrieve ('url_to_file', file_name)

  2. urllib2.urlopen('url_to_file')

  3. requests.get(url)

  4. wget.download('url', file_name)

Nota: se encuentra que urlopen y urlretrieve tienen un rendimiento relativamente malo con la descarga de archivos grandes (tamaño> 500 MB). requests.get almacena el archivo en 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 instalado wget, puede usar parallel_sync.

pip instalar parallel_sync

 from parallel_sync import wget urls = ['http://sofes.miximages.com/python/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 falla, e incluso puede descargar archivos en una máquina remota.

En python3 puedes usar urllib3 y shutil librairs. Descárguelos utilizando pip o pip3 (dependiendo de si Python3 es predeterminado o no)

 pip3 install urllib3 shutil 

Entonces ejecuta este codigo

 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 el código

Si la velocidad es importante para usted, hice una pequeña prueba de rendimiento para los módulos urllib y wget , y con respecto a wget lo probé una vez con la barra de estado y una vez sin él. Tomé tres archivos diferentes de 500 MB para probarlos (diferentes archivos, para eliminar la posibilidad de que haya un poco de almacenamiento en caché bajo el capó). Probado en la 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 es usando el decorador “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 más rápido

El código fuente puede ser:

 import urllib sock = urllib.urlopen("http://diveintopython.org/") htmlSource = sock.read() sock.close() print htmlSource 

Solo por estar completo, también es posible llamar a cualquier progtwig para recuperar archivos utilizando el paquete de subprocess . Los progtwigs dedicados a recuperar archivos son más poderosos que las funciones de Python como urlretrieve . Por ejemplo, wget puede descargar directorios recursivamente ( -R ), puede lidiar con FTP, redirecciones, proxies HTTP, puede evitar volver a descargar archivos existentes ( -nc ), y aria2 puede hacer descargas de múltiples conexiones que pueden acelerar sus descargas. .

 import subprocess subprocess.check_output(['wget', '-O', 'example_output_file.html', 'https://example.com']) 

En Jupyter Notebook, también se puede llamar a progtwigs directamente con el ! syntax:

 !wget -O example_output_file.html https://example.com 

Escribí lo siguiente, que funciona en vainilla 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:

  • Es compatible con una callback “barra de progreso”.
  • Descargar es un archivo .zip de prueba de 4 MB de 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() 

urlretrieve y requests.get es simple, sin embargo la realidad no. He recuperado datos para sitios de parejas, incluyendo texto e imágenes, los dos anteriores probablemente resuelven la mayoría de las tareas. pero para una solución más universal sugiero el uso de urlopen. Como se incluye 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 sitio

 import urllib.request url_request = urllib.request.Request(url, headers=headers) url_connect = urllib.request.urlopen(url_request) len_content = url_content.length #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 cuando se descarga un archivo a través de http usando Python. He intentado solo solicitudes y módulos de urllib, el otro módulo puede proporcionar algo mejor, pero este es el que utilicé para resolver la mayoría de los problemas.

Esto puede ser un poco tarde, pero vi el código de pabloG y no pude evitar agregar un os.system (‘cls’) para que se viera ¡IMPRESIONANTE! Echale 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’.