Запросы - действительно хорошая библиотека. Я бы хотел использовать ее для загрузки больших файлов (> 1 ГБ). Проблема в том, что невозможно сохранить весь файл в памяти; мне нужно читать его частями. И это проблема со следующим кодом:
import requests
defDownloadFile(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
По какой-то причине это не работает таким образом; он по-прежнему загружает ответ в память, прежде чем он будет сохранен в файл.
Переведено автоматически
Ответ 1
С помощью следующего потокового кода использование памяти Python ограничено независимо от размера загружаемого файла:
defdownload_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() withopen(local_filename, 'wb') as f: for chunk in r.iter_content(chunk_size=8192): # If you have chunk encoded response uncomment if # and set chunk_size parameter to None. #if chunk: f.write(chunk) return local_filename
Обратите внимание, что количество байтов, возвращаемых с помощью iter_content, не совсем то chunk_size; ожидается, что это будет случайное число, которое часто намного больше и, как ожидается, будет отличаться на каждой итерации.
defdownload_file(url): local_filename = url.split('/')[-1] with requests.get(url, stream=True) as r: withopen(local_filename, 'wb') as f: shutil.copyfileobj(r.raw, f)
return local_filename
Это позволяет передавать файл на диск без использования избыточной памяти, а код прост.
Примечание: Согласно документации, Response.raw будет не декодироватьgzip, а deflate передавать-encodings, поэтому вам нужно будет сделать это вручную.
Ответ 3
Не совсем то, что просил OP, но ... это до смешного легко сделать с помощью urllib:
Или этим способом, если вы хотите сохранить его во временный файл:
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)
Я наблюдал за процессом:
watch 'ps -p 18647 -o pid,ppid,pmem,rsz,vsz,comm,args; ls -al *.iso'
И я увидел, что файл растет, но использование памяти осталось на уровне 17 МБ. Я что-то упускаю?
Ответ 4
Размер вашего блока может быть слишком большим, вы пробовали удалять его - возможно, 1024 байта за раз? (также вы могли бы использовать with для приведения в порядок синтаксиса)
defDownloadFile(url): local_filename = url.split('/')[-1] r = requests.get(url) withopen(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
Кстати, как вы определяете, что ответ был загружен в память?
Звучит так, как будто python не сбрасывает данные в файл, из других вопросов SO вы могли бы попробовать f.flush() и os.fsync() принудительно записать файл и освободить память;
withopen(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())