使用requests下载大文件并传入subprocess标准输入的问题求助
使用requests流式下载大文件并传递给subprocess的优化方案
我之前也踩过这个坑!你写的那段代码逻辑上没问题,但速度慢到让人抓狂,核心问题出在r.raw的默认行为上——它的缓冲区太小了,导致子进程(比如wc)一直在频繁处理 tiny 的数据块,光是系统调用的开销就把速度拖垮了。
问题根源
requests.get(..., stream=True)返回的r.raw是urllib3.response.HTTPResponse对象,它默认的读取块非常小,而且没有开启高效缓冲。当你直接把它传给subprocess.Popen的stdin时,子进程会不断触发小数据读取操作,频繁的用户态/内核态切换严重拉低了整体效率。
优化方案
下面两种方法都能解决这个问题,核心思路都是增大数据传输的块大小,减少IO操作的次数:
方法1:手动分块读取并写入子进程
用requests的iter_content方法指定合适的块大小,分块把数据写入子进程的标准输入:
import requests import subprocess # 替换成你的大文件URL url = "http://example.com/large-file" # stream=True开启流式下载 r = requests.get(url, stream=True) # 关键:自动解压服务器返回的压缩内容(比如gzip) r.raw.decode_content = True # 启动子进程,这里以wc为例 p = subprocess.Popen(["wc"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) # 设置块大小,建议64KB~1MB之间,根据内存情况调整 chunk_size = 64 * 1024 for chunk in r.iter_content(chunk_size=chunk_size): if chunk: # 过滤空块 p.stdin.write(chunk) # 关闭stdin,告诉子进程输入结束 p.stdin.close() # 获取子进程输出 output, _ = p.communicate() print(output.decode("utf-8"))
方法2:用shutil高效复制文件对象
shutil.copyfileobj是Python标准库提供的高效文件对象复制工具,它会自动使用合适的缓冲区大小,代码更简洁:
import requests import subprocess import shutil url = "http://example.com/large-file" r = requests.get(url, stream=True) r.raw.decode_content = True p = subprocess.Popen(["wc"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) # 用copyfileobj完成高效数据传输,也可以指定bufsize参数调整缓冲区 shutil.copyfileobj(r.raw, p.stdin) p.stdin.close() output, _ = p.communicate() print(output.decode("utf-8"))
额外注意事项
r.raw.decode_content = True一定要加!如果服务器返回的是压缩格式(比如gzip),这个设置会自动解压,避免传给子进程压缩后的原始数据。- 块大小不用盲目调大,一般64KB就能显著提升速度,太大可能会占用过多内存,反而影响性能。
- 如果网络不稳定,建议添加异常处理(比如捕获
requests.exceptions.RequestException),避免中途崩溃。
内容的提问来源于stack exchange,提问作者Congbin Guo
相关产品推荐
相关产品推荐

