问题:使用请求在python中下载大文件
请求是一个非常不错的库。我想用它来下载大文件(> 1GB)。问题是不可能将整个文件保留在内存中,我需要分块读取它。这是以下代码的问题
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下载大文件,则可以在此处找到它。它支持多线程和重新连接(它确实监视连接),还可以为下载任务调整套接字参数。
回答 0
使用以下流代码,无论下载文件的大小如何,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 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
; 它应该是一个通常更大的随机数,并且在每次迭代中都应该有所不同。
见https://requests.readthedocs.io/en/latest/user/advanced/#body-content-workflow和https://requests.readthedocs.io/en/latest/api/#requests.Response.iter_content进一步参考。
回答 1
如果使用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
这样就无需占用过多内存就可以将文件流式传输到磁盘,并且代码很简单。
回答 2
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 MB。我想念什么吗?
回答 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())