Скачать большой файл в Python с запросами
Requests - это действительно хорошая библиотека. Я хотел бы использовать его для загрузки больших файлов (> 1 ГБ). Проблема в том, что невозможно сохранить весь файл в памяти, мне нужно прочитать его порциями. И это проблема со следующим кодом
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
По какой-то причине это не работает таким образом. Он по-прежнему загружает ответ в память, прежде чем сохранить его в файл.
ОБНОВИТЬ
Если вам нужен маленький клиент (Python 2.x/3.x), который может загружать большие файлы с FTP, вы можете найти его здесь. Он поддерживает многопоточность и повторное соединение (он контролирует соединения), а также настраивает параметры сокета для задачи загрузки.
Ответы
Ответ 1
При использовании следующего потокового кода использование памяти Python ограничено независимо от размера загружаемого файла:
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
Обратите внимание, что число байтов, возвращаемых с использованием iter_content
, не совсем то, что chunk_size
; ожидается, что это будет случайное число, которое часто намного больше, и ожидается, что оно будет различным на каждой итерации.
См. Http://docs.python-requests.org/en/latest/user/advanced/#body-content-workflow для дальнейшего ознакомления.
Ответ 2
Это намного проще, если вы используете Response.raw
и 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
Это потоковое файл на диск без использования чрезмерной памяти, и код прост.
Ответ 3
Размер вашего куска может быть слишком большим, вы пытались удалить его - может быть, 1024 байта за раз? (также вы можете использовать with
, чтобы убрать синтаксис)
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
Кстати, как вы вывели, что ответ был загружен в память?
Звучит так, как будто python не сбрасывает данные в файл, из других SO вопросов вы можете попробовать f.flush()
и os.fsync()
, чтобы заставить файл писать и свободной памяти;
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())
Ответ 4
Не совсем то, о чем спрашивал OP, но... это смешно легко сделать с помощью 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)
Или, если вы хотите сохранить его во временный файл:
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 МБ. Я что-то пропустил?