最稳妥的下载方式是用 requests.get() 配合 stream=true、检查 status_code、用 r.content 写入 'wb' 文件,并设置 timeout 和异常处理。

用 requests.get() 下载二进制内容最稳妥
直接用 urllib.request.urlopen() 读取 URL 内容再写入文件,容易在遇到重定向、编码异常或大文件时出错。推荐用 requests 库的 get() 方法配合 stream=True 参数,避免内存爆掉。
- 必须加
stream=True,否则整个响应体加载进内存,下载 500MB 文件会卡死 - 检查
r.status_code == 200再写入,否则可能把 404 页面存成“正常文件” - 用
r.content(不是r.text)写入文件,尤其对图片、PDF、ZIP 等非文本资源,r.text会触发错误解码
写入文件时注意打开模式和编码
文本类资源(如 CSV、JSON、HTML)可按需指定编码;但绝大多数情况——尤其是不确定来源编码或下载二进制文件时——必须用 'wb' 模式,否则会报 UnicodeEncodeError 或损坏文件。
- 文本资源:用
open(...,'w', encoding='utf-8'),并确保r.text可靠(比如已知服务返回 UTF-8) - 通用做法:一律用
open(...,'wb')+r.content,兼容所有类型 - 别依赖
r.encoding自动推断,它经常是None或错的,特别是没有Content-Type头时
处理重定向和超时不能靠默认值
requests.get() 默认跟随重定向(allow_redirects=True),但某些 API 返回 302 后跳转到临时签名链接,若不捕获原始响应头,就拿不到关键信息(比如 Content-Disposition 中建议的文件名)。
- 需要原始响应头时,显式设
allow_redirects=False,再手动处理r.headers.get('Location') - 务必设
timeout=(3, 30):第一个数字是连接超时,第二个是读取超时,避免卡住整个程序 - 不要只写
timeout=30,这只会限制总耗时,连接失败仍可能等很久
小文件可一行搞定,大文件必须分块写入
小于 1MB 的资源可以偷懒用 open(...,'wb').write(r.content);但超过几 MB 就得用 iter_content(chunk_size=8192) 流式写入,否则要么内存炸,要么被 OOM Killer 杀掉进程。
- 典型 chunk_size 是
8192(8KB),太小增加 I/O 次数,太大没意义 - 写入前先创建目录:
os.makedirs(os.path.dirname(filepath), exist_ok=True),避免路径不存在报错 - 别忽略异常:
requests.exceptions.RequestException要包住,网络问题、DNS 失败、SSL 验证失败都归这类
stream=True 和 timeout 设置,一跑就卡住或失败,而不是报错——这会让调试变得特别隐蔽。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











