失败任务应使用 requests + urllib3.Retry 实现指数退避重试,设置 total、status_forcelist 和 backoff_factor;任务状态需持久化至 Redis 或原子写入 JSONL 文件;异步爬虫中用 asyncio.timeout 包裹请求并 create_task 提交重试;超限失败任务应入死信队列并记录原因。

失败任务怎么自动重试?用 requests + 指数退避最靠谱
直接硬循环重试会触发反爬限流,requests 默认不带重试逻辑,必须手动加。核心是控制重试次数、间隔和异常类型,别一遇到 ConnectionError 或 Timeout 就狂试。
- 用
urllib3.Retry配合requests.adapters.HTTPAdapter,比自己写while循环更可控 - 重点设
backoff_factor(比如0.3),它会让第 n 次重试等待backoff_factor * (2^(n-1))秒,避免雪崩 - 只对网络层错误重试,别对
404或403重试——那是业务状态,不是临时故障
from requests.adapters import HTTPAdapter from urllib3.util.retry import Retrysession = requests.Session() retry_strategy = Retry( total=3, status_forcelist=[429, 500, 502, 503, 504], backoff_factor=0.3, ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("http://", adapter) session.mount("https://", adapter)
任务状态怎么持久化?别全靠内存存 queue
程序崩溃或重启后,内存里的待爬队列就丢了。得把「已取未完成」「失败待重试」的任务落地,否则回收机制形同虚设。
-
Redis是首选:用LPUSH/RPOP做可靠队列,配合BRPOP阻塞读取,天然支持多进程/多机消费 - 如果不用 Redis,至少用文件+原子写入:把任务 ID 和元数据(URL、重试次数、上次失败时间)存成 JSONL,每次消费前
flock加锁 - 别用
pickle序列化任务对象——跨 Python 版本或代码变更后很可能反序列化失败
asyncio 爬虫里怎么安全回收失败请求?小心协程泄漏
异步环境下,没处理完的 async with session.get(...) 可能卡住整个事件循环,而且 try/except 不捕获所有异常类型。
- 必须用
asyncio.timeout()包裹请求,防止某个 URL 死等(比如 DNS 卡住) - 失败后别直接
await asyncio.sleep()然后重试——这会阻塞当前协程;改用asyncio.create_task()提交重试任务,并加个最大重试计数限制 - 所有异常要显式 catch
asyncio.TimeoutError、aiohttp.ClientError、UnicodeDecodeError(响应编码异常)
import asyncio import aiohttpasync def fetch_with_retry(session, url, max_retries=2): for i in range(max_retries + 1): try: async with asyncio.timeout(10): async with session.get(url) as resp: return await resp.text() except (aiohttp.ClientError, asyncio.TimeoutError, UnicodeDecodeError) as e: if i == max_retries: raise e await asyncio.sleep(0.5 * (2 ** i)) # 指数退避 return None
重试太多次还是失败?该进死信队列还是丢弃?
不是所有失败都要无限重试。持续失败的任务可能是目标页面已下线、参数非法、或风控永久封禁,继续重试只会浪费资源、暴露 IP。
立即学习“Python免费学习笔记(深入)”;
- 设定硬性重试上限(比如 5 次),超限后写入「死信队列」(
redis list名为dead_tasks),供人工排查或定时归档 - 记录失败原因(HTTP 状态码、异常类型、响应头
X-RateLimit-Remaining等),别只记个"failed" - 对高频失败的目标域名,自动触发降频策略:暂停该域名任务 5 分钟,或切到备用代理池
真正难的不是写重试逻辑,而是判断什么时候该停——这需要结合日志、监控和业务语义,纯技术方案兜不住所有情况。










