Requests est une très belle bibliothèque. J'aimerais l'utiliser pour télécharger de gros fichiers (> 1 Go). Le problème est qu'il n'est pas possible de garder tout le fichier en mémoire, je dois le lire en morceaux. Et c'est un problème avec le code suivant
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
Pour une raison quelconque, cela ne fonctionne pas de cette façon. Il charge toujours la réponse en mémoire avant de l'enregistrer dans un fichier.
UPDATE
Si vous avez besoin d’un petit client (Python 2.x /3.x) capable de télécharger de gros fichiers à partir de FTP, vous pouvez le trouver ici . Il prend en charge le multithreading et les reconnexions (il surveille les connexions) et il ajuste également les paramètres de socket pour la tâche de téléchargement.
Avec le code de transmission en continu suivant, l'utilisation de la mémoire Python est restreinte quelle que soit la taille du fichier téléchargé:
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
Notez que le nombre d'octets renvoyés à l'aide de iter_content
n'est pas exactement le chunk_size
; on s'attend à ce qu'il s'agisse d'un nombre aléatoire souvent beaucoup plus grand et différent à chaque itération.
Voir http://docs.python-requests.org/en/latest/user/advanced/#body-content-workflow pour plus de détails.
C'est beaucoup plus facile si vous utilisez Response.raw
et shutil.copyfileobj()
:
_import requests
import shutil
def download_file(url):
local_filename = url.split('/')[-1]
with requests.get(url, stream=True) as r:
with open(local_filename, 'wb') as f:
shutil.copyfileobj(r.raw, f)
return local_filename
_
Cela diffuse le fichier sur le disque sans utiliser trop de mémoire et le code est simple.
Ce n'est pas exactement ce que demandait OP, mais ... c'est ridiculement facile de le faire avec 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)
Ou de cette façon, si vous voulez l'enregistrer dans un fichier temporaire:
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)
J'ai regardé le processus:
watch 'ps -p 18647 -o pid,ppid,pmem,rsz,vsz,comm,args; ls -al *.iso'
Et j'ai vu le fichier grandir, mais l'utilisation de la mémoire est restée à 17 Mo. Est-ce que je manque quelque chose?
La taille de votre bloc est peut-être trop importante. Avez-vous essayé de le supprimer - peut-être 1024 octets à la fois? (vous pouvez aussi utiliser with
pour ranger la syntaxe)
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
Incidemment, comment en déduisez-vous que la réponse a été chargée en mémoire?
On dirait que python ne vide pas les données dans un fichier, mais d’autres questions SO , vous pouvez essayer f.flush()
et os.fsync()
pour forcer le fichier à écrire et libérer de la mémoire;
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())