Descargar archivo grande en python con peticiones

Solicitudes es una biblioteca muy agradable. Me gustaría usarlo para descargar archivos grandes (> 1GB). El problema es que no es posible mantener todo el archivo en la memoria. Necesito leerlo en trozos. Y este es un problema con el siguiente código.

import requests def DownloadFile(url) local_filename = url.split('/')[-1] r = requests.get(url) f = open(local_filename, 'wb') for chunk in r.iter_content(chunk_size=512 * 1024): if chunk: # filter out keep-alive new chunks f.write(chunk) f.close() return 

Por alguna razón no funciona de esta manera. Todavía carga la respuesta en la memoria antes de guardarla en un archivo.

ACTUALIZAR

Si necesita un cliente pequeño (Python 2.x /3.x) que pueda descargar archivos grandes desde FTP, puede encontrarlo aquí . Admite subprocesos múltiples y reconecta (monitorea las conexiones) y también ajusta los parámetros de socket para la tarea de descarga.

Con el siguiente código de transmisión, el uso de la memoria de Python está restringido independientemente del tamaño del archivo descargado:

 def download_file(url): local_filename = url.split('/')[-1] # NOTE the stream=True parameter below with requests.get(url, stream=True) as r: r.raise_for_status() with open(local_filename, 'wb') as f: for chunk in r.iter_content(chunk_size=8192): if chunk: # filter out keep-alive new chunks f.write(chunk) # f.flush() return local_filename 

Tenga en cuenta que el número de bytes devueltos usando iter_content no es exactamente el chunk_size ; se espera que sea un número aleatorio que a menudo es mucho más grande, y se espera que sea diferente en cada iteración.

Consulte http://docs.python-requests.org/en/latest/user/advanced/#body-content-workflow para obtener más información.

Es mucho más fácil si usa Response.raw y shutil.copyfileobj() :

 import requests import shutil def download_file(url): local_filename = url.split('/')[-1] r = requests.get(url, stream=True) with open(local_filename, 'wb') as f: shutil.copyfileobj(r.raw, f) return local_filename 

Esto transmite el archivo al disco sin utilizar una memoria excesiva, y el código es simple.

Su tamaño de trozo podría ser demasiado grande, ¿ha intentado eliminar eso, tal vez 1024 bytes a la vez? (También, puedes usar with para ordenar la syntax)

 def DownloadFile(url): local_filename = url.split('/')[-1] r = requests.get(url) with open(local_filename, 'wb') as f: for chunk in r.iter_content(chunk_size=1024): if chunk: # filter out keep-alive new chunks f.write(chunk) return 

Por cierto, ¿cómo deduce que la respuesta se ha cargado en la memoria?

Parece que Python no está vaciando los datos para archivar, de otras preguntas de SO , puede probar f.flush() y os.fsync() para forzar la escritura y la memoria libre del archivo;

  with open(local_filename, 'wb') as f: for chunk in r.iter_content(chunk_size=1024): if chunk: # filter out keep-alive new chunks f.write(chunk) f.flush() os.fsync(f.fileno()) 

No es exactamente lo que estaba preguntando OP, pero … es ridículamente fácil hacerlo con urllib :

 from urllib.request import urlretrieve url = 'http://mirror.pnl.gov/releases/16.04.2/ubuntu-16.04.2-desktop-amd64.iso' dst = 'ubuntu-16.04.2-desktop-amd64.iso' urlretrieve(url, dst) 

O de esta manera, si desea guardarlo en un archivo temporal:

 from urllib.request import urlopen from shutil import copyfileobj from tempfile import NamedTemporaryFile url = 'http://mirror.pnl.gov/releases/16.04.2/ubuntu-16.04.2-desktop-amd64.iso' with urlopen(url) as fsrc, NamedTemporaryFile(delete=False) as fdst: copyfileobj(fsrc, fdst) 

Observé el proceso:

 watch 'ps -p 18647 -o pid,ppid,pmem,rsz,vsz,comm,args; ls -al *.iso' 

Y vi crecer el archivo, pero el uso de memoria se mantuvo en 17 MB. ¿Me estoy perdiendo de algo?