如何给 Python 爬虫添加重试机制处理网络不稳定?

文章导读
给 Python 爬虫添加重试机制最推荐直接使用 requests 库自带的 HTTPAdapter 配合 urllib3.util.retry.Retry 类,适用于大多数同步 HTTP 请求场景。需要注意设置最大重试次数和退避策略,避免对目标服务器造成过大压力或被封禁。
📋 目录
  1. A 命令速用版
  2. B 为什么会这样
  3. C 分步处理
  4. D 怎么验证是否生效
  5. E 常见坑
  6. F 常见问题
  7. G 参考来源
A A

给 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:挂载适配器到会话

如何给 Python 爬虫添加重试机制处理网络不稳定?

创建 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/