给 Python 爬虫添加重试机制最推荐直接使用 requests 库自带的 HTTPAdapter 配合 urllib3.util.retry.Retry 类,适用于大多数同步 HTTP 请求场景。需要注意设置最大重试次数和退避策略,避免对目标服务器造成过大压力或被封禁。
先说结论:通过配置会话对象的重试策略来处理网络波动,比在循环中手动写 try-except 更规范且易于维护。
- 适合:基于 requests 库的同步爬虫项目,需要处理临时性 5xx 错误或连接超时。
- 先看:确认目标站点是否允许重试,避免高频请求触发反爬机制。
- 建议:配合指数退避算法设置等待时间,不要立即发起下一次请求。
命令速用版
如果当前项目未安装依赖,先通过 pip 安装基础库。requests 库通常已内置 urllib3,无需额外安装重试模块。
pip install requests若需要更复杂的重试逻辑(如重试特定函数、异步支持),可选装 tenacity 库。
pip install tenacity为什么会这样
网络请求失败通常由临时性波动引起,而非代码逻辑错误。常见的失败原因包括 DNS 解析超时、服务器瞬时高负载返回 503、或中间网络节点丢包。
直接在代码中硬编码循环重试会导致逻辑耦合,难以统一控制重试次数和间隔。使用标准库提供的重试机制可以将网络稳定性处理与业务逻辑分离,便于后续调整策略而不影响核心抓取代码。
分步处理
以下步骤基于 requests 库实现会话级重试配置,适用于大多数爬虫场景。
步骤 1:导入必要模块
从 urllib3.util.retry 导入 Retry 类,从 requests.adapters 导入 HTTPAdapter。
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
import requests步骤 2:定义重试策略
设置 total 为最大重试次数,status_forcelist 指定需要重试的 HTTP 状态码,backoff_factor 设置退避因子。
retry_strategy = Retry(
total=3,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["HEAD", "GET", "OPTIONS"],
backoff_factor=1
)步骤 3:挂载适配器到会话
创建 Session 对象,将配置好的重试策略挂载到 HTTP 和 HTTPS 协议上。
session = requests.Session()
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)步骤 4:使用会话发起请求
后续所有通过该 session 发起的请求都会自动应用重试策略。
response = session.get("https://example.com")怎么验证是否生效
开启 requests 的调试日志,观察 urllib3 输出的重试信息。在代码中加入以下配置即可看到详细的重试过程。
import logging
logging.basicConfig(level=logging.DEBUG)
当触发重试时,日志中会出现 Retrying 关键字,包含重试次数和等待时间。若目标网站稳定,日志中不应出现重试记录;若模拟网络波动,应看到请求自动重新发起。
常见坑
无限重试风险:如果 total 设置过大或未设置 backoff_factor,可能在网络持续故障时长时间阻塞程序,甚至被目标服务器封禁 IP。
错误状态码误判:不要对 404 或 403 错误进行重试。404 表示资源不存在,重试无效;403 表示权限禁止,重试可能加剧封禁风险。status_forcelist 应仅包含 5xx 服务端错误或 429 请求过多。
POST 请求重试:默认情况下重试机制可能不包含 POST 请求,因为 POST 不是幂等操作。若需重试 POST,需在 allowed_methods 中显式添加 "POST",并确保业务逻辑支持重复提交。
常见问题
异步爬虫 aiohttp 怎么加重试?
aiohttp 没有内置像 requests 那样方便的重试适配器,通常配合 tenacity 库使用装饰器包装异步函数,或手动在循环中捕获异常并 await asyncio.sleep。
重试间隔时间设置多少合适?
建议采用指数退避,例如 1 秒、2 秒、4 秒。backoff_factor=1 时,等待时间为 {total - retry} * backoff_factor 秒。具体数值需根据目标站点承受能力调整,公开资料中没有看到可靠的量化数据表明固定值最优。
如何区分网络错误和代码错误?
网络错误通常抛出 requests.exceptions.RequestException 子类,如 ConnectionError 或 Timeout。代码逻辑错误通常抛出 ValueError 或 KeyError。重试机制应仅捕获网络相关异常。
参考来源
- requests 官方文档,Advanced Usage - Session Objects,https://requests.readthedocs.io/
- urllib3 官方文档,Retry Configuration,https://urllib3.readthedocs.io/
- tenacity 官方文档,Retry Library for Python,https://tenacity.readthedocs.io/