
Scrapy 的 errback 并非“每次异常都立即触发”,而是在请求生命周期中异常未被中间件捕获且最终放弃重试时才执行;RetryMiddleware 返回新 Request 会中断当前流程,使 errback 暂缓调用甚至完全不触发。
scrapy 的 errback 并非“每次异常都立即触发”,而是在请求生命周期中异常未被中间件捕获且最终放弃重试时才执行;retrymiddleware 返回新 request 会中断当前流程,使 errback 暂缓调用甚至完全不触发。
在 Scrapy 请求处理链中,errback 的调用时机高度依赖于异常的传播路径与下载器中间件(尤其是 RetryMiddleware)的干预行为,它并非无条件、无延迟地响应每一次异常。
✅ 正确理解:errback 是“兜底回调”,而非“异常监听器”
根据官方文档与源码逻辑:
- 当请求在下载阶段抛出异常(如
TimeoutError、DNSLookupError、ConnectionRefusedError等),该异常首先交由process_exception()方法链处理; - 若某中间件(如
RetryMiddleware)在process_exception()中返回一个新的Request对象,Scrapy 将立即终止当前请求的异常处理流程,转而将新 Request 重新入队、调度、重试——此时原 Request 的errback不会被调用; - 只有当所有中间件均未处理该异常(即
process_exception()全部返回None),且该请求已耗尽最大重试次数(RETRY_TIMES)或被明确标记为不可重试时,Scrapy 才会最终调用原始 Request 的errback函数。
? 关键点:
errback的触发前提是“该 Request 实例彻底退出下载流程”,而非“只要发生异常就调用”。
? 示例说明:超时场景下的典型流程
# spider.py
def start_requests(self):
yield scrapy.Request(
url="https://httpbin.org/delay/10",
callback=self.parse,
errback=self.handle_error,
meta={"download_timeout": 3},
dont_filter=True
)
def handle_error(self, failure):
self.logger.error("Request failed permanently: %s", failure.getErrorMessage())
# 此处仅在重试全部失败后执行配合默认 RetryMiddleware(RETRY_TIMES=2):
- 第一次请求 → 超时 →
RetryMiddleware.process_exception()返回重试 Request → 原errback不触发; - 第二次请求 → 再次超时 →
RetryMiddleware再次返回重试 Request; - 第三次请求(即第 2 次重试后)→ 仍超时 →
RetryMiddleware判定已达上限,返回None→ 异常上浮 → 最终调用self.handle_error(failure)。
✅ 因此,handle_error 在该例中仅被执行 1 次,且发生在第三次尝试失败之后。
⚠️ 特别注意:IgnoreRequest 是例外路径
你引用的文档段落提及 IgnoreRequest,这是另一条独立异常路径:
-
IgnoreRequest通常由DownloaderMiddleware.process_request()主动抛出(例如OffsiteMiddleware阻止跨域请求); - 它不经过
RetryMiddleware重试逻辑,而是直接进入process_exception()链; - 若无人处理,最终触发
errback—— 这正是文档强调该场景的原因,但不能推广至网络层异常(如 Timeout)。
✅ 最佳实践建议
-
不要在
errback中做“重试逻辑”:重试应由RetryMiddleware或自定义中间件统一管控;errback适合日志记录、告警、存档失败快照或降级处理(如切换备用 API); -
需精细控制重试?请定制 Downloader Middleware:如代理失效检测、403/429 状态码重试、IP 黑名单剔除等,必须打破
RetryMiddleware与HttpProxyMiddleware的隔离,实现异常-状态-代理三位一体调度; -
调试技巧:启用
LOG_LEVEL=DEBUG,观察日志中Retrying和Gave up的出现时机,可清晰验证errback是否如期触发。
综上,errback 是 Scrapy 请求健壮性的最后一道保险,它的沉默恰恰说明重试机制正在有效工作;而它的响起,则标志着系统已确认该请求“不可恢复”——理解这一设计哲学,是写出高可用爬虫的关键前提。

















