Python使用requests.get下载图片未完全加载致0字节、空白异常问题
Python requests 偶发下载图片得到0字节/空白文件问题修复
问题现象
调用requests.get从网站下载图片时,偶发获取不到有效数据:
- 部分场景下下载得到的文件大小为0字节,无任何有效内容
- 部分场景下载流程正常走完,但打开后图片为空白
失败案例日志
Remote URL https://gd3.alicdn.com/imgextra/i3/917765107/O1CN01FUj4ea1nb3SEtYlCB_!!917765107.jpg filename 1657077308/693904.jpg size 0
正常案例日志
Remote URL https://gd1.alicdn.com/imgextra/i1/917765107/O1CN016sR7UO1nb3SNr1Fo2_!!917765107.jpg filename 1657077186/735590.jpg size 14032
原有问题代码
初始实现逻辑:
# Getting image from remote url response = requests.get(remote_url, stream=True) # Get the content of image imageResponse = response.raw len(response.content)
复现空白图片问题的测试代码:
remote_url = "https://img.alicdn.com/imgextra/i3/917765107/O1CN01WwxHa81nb3SLAupPq_!!917765107.jpg" imageResponse = requests.get(remote_url, stream=True) print(len(imageResponse.content))
问题根因
注意:问题和图片体积大、加载耗时没有直接关系,核心是代码逻辑存在漏洞
- 误用
stream=True参数:开启流模式后,requests不会在请求返回时自动拉取完整响应体,直接读取response.raw或在内容未加载完成时访问response.content,只会拿到截断的不完整数据 - 缺少反爬适配:阿里系CDN默认校验请求头特征,使用requests默认标识、不带Referer的请求有概率被反爬策略拦截,返回空响应或者验证页面内容,存为图片后就会出现0字节或空白问题
- 无响应校验逻辑:遇到网络错误、CDN节点故障返回4xx/5xx响应时,没有判断状态码直接读取内容,拿到的本身就不是图片数据
- 无超时、重试机制:遇到网络抖动、CDN节点临时响应慢时请求直接中断,没有重试逻辑就会直接触发下载失败
修复方案
核心调整点
- 普通尺寸图片(单张10MB以内)不要开启
stream=True,默认配置下requests会自动完成全量响应体加载后再返回,不会出现内容截断 - 请求时携带常规浏览器UA和对应站点的Referer头,避免被CDN反爬拦截
- 配置合理的超时时间,避免请求无限挂起,一般连接超时设3秒、读取超时设10-30秒即可覆盖绝大多数场景
- 增加自动重试逻辑,针对CDN临时错误、网络波动场景自动重试2-3次,大幅降低偶发失败概率
- 响应内容校验:先判断状态码是否为200,再校验内容长度是否非0、响应头的Content-Type是否为图片类型,确认是有效图片数据后再写入文件
可直接复用的实现代码
import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry def download_image(remote_url: str, save_path: str, max_retry: int = 3) -> bool: # 初始化请求会话,配置重试策略 session = requests.Session() retry_config = Retry( total=max_retry, backoff_factor=1, # 重试间隔按1/2/4秒递增,避免频繁请求触发拦截 allowed_methods=["GET"], status_forcelist=[429, 500, 502, 503, 504] # 仅对服务端临时错误触发重试 ) session.mount("https://", HTTPAdapter(max_retries=retry_config)) session.mount("http://", HTTPAdapter(max_retries=retry_config)) try: response = session.get( remote_url, timeout=(3, 20), # 连接超时3s,读取超时20s headers={ # 模拟常规浏览器请求头,绕过基础反爬 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Referer": "https://www.taobao.com/" } ) # 状态码校验,非200响应直接抛错 response.raise_for_status() # 空内容校验 img_content = response.content if len(img_content) == 0: raise ValueError("Empty response content") # 响应类型校验,避免把错误页HTML存成图片 content_type = response.headers.get("Content-Type", "") if not content_type.startswith("image/"): raise ValueError(f"Invalid content type: {content_type}, not image") # 二进制写入本地文件 with open(save_path, "wb") as f: f.write(img_content) return True except Exception as e: print(f"Download image {remote_url} failed: {str(e)}") return False finally: session.close()
超大图下载补充说明
如果需要下载单张几十MB以上的超大图必须开stream=True,不要直接读response.content,需要按固定块大小逐块读取写入文件,下载完成后对比本地文件大小和响应头Content-Length字段值,两者一致才判定为下载成功,否则触发重试。
内容的提问来源于stack exchange,提问作者Sohaib Anwaar
相关产品推荐
相关产品推荐

