
Scrapy 的 errback 仅在请求最终失败且未被中间件拦截处理时触发,它不会在每次重试前调用;RetryMiddleware 成功返回新 Request 时,原请求流程终止,errback 完全不执行。
scrapy 的 `errback` 仅在请求最终失败且未被中间件拦截处理时触发,它不会在每次重试前调用;retrymiddleware 成功返回新 request 时,原请求流程终止,errback 完全不执行。
在 Scrapy 请求生命周期中,errback 并非“异常发生即调用”的兜底钩子,而是一个终局性错误回调——它的触发有严格前提:请求必须经历完整下载链路(包括所有 downloader middleware)后,仍因不可恢复的异常(如 DNS 失败、连接超时、SSL 错误等)导致响应无法生成,且该异常未被任何中间件的 process_exception() 方法捕获并处理。
关键逻辑链如下:
请求发起 → 下载器执行 → 异常抛出
当 Twisted 底层抛出TimeoutError、DNSLookupError、TCPTimedOutError等网络层异常时,Scrapy 下载器会将异常传递给已启用的 downloader middleware 的process_exception(request, exception, spider)方法。-
RetryMiddleware 的介入是“中断式重试”
RetryMiddleware的process_exception()在检测到可重试异常(如默认配置中的TimeoutError)时,不会让异常继续向上传播,而是直接return scrapy.Request(...)—— 这个新 Request 会被重新调度进入下载队列,原 Request 的生命周期就此终结。此时:- 原 Request 的
callback和errback均不再执行; - 新 Request 拥有独立的
callback/errback,其meta可继承(需显式传递),但状态完全隔离。
- 原 Request 的
-
errback仅在“重试耗尽”或“异常被忽略”时触发
仅当满足以下任一条件时,errback才会被调用:- RetryMiddleware 被禁用(
RETRY_ENABLED = False),或异常不在其RETRY_HTTP_CODES/RETRY_EXCEPTIONS列表中(例如自定义异常未配置); - RetryMiddleware 已达到最大重试次数(
RETRY_TIMES),且最后一次重试仍失败 → 此时异常穿透 middleware 链,最终交由Request.errback处理; - 其他 middleware(如自定义代理中间件)主动
raise IgnoreRequest且无人处理,按文档所述触发errback(注意:这是特例,与网络异常无关)。
- RetryMiddleware 被禁用(
✅ 正确使用示例(超时自动重试 + 终局 errback):
def start_requests(self):
yield scrapy.Request(
url="https://httpbin.org/delay/10",
callback=self.parse,
errback=self.handle_failure,
meta={"download_timeout": 5}, # 强制5秒超时
dont_filter=True
)
def handle_failure(self, failure):
# 仅当所有重试均失败后才会进入此处
request = failure.request
if failure.check(scrapy.exceptions.TooManyRedirects):
self.logger.warning("Too many redirects: %s", request.url)
elif failure.check(twisted.internet.error.TimeoutError):
self.logger.error("Final timeout after retries: %s", request.url)
# 可在此发起降级请求(如换代理、切备用域名)
yield scrapy.Request(
url=request.url.replace("https://", "http://"),
callback=self.parse_fallback,
errback=self.fallback_failed
)
⚠️ 注意事项:
- 不要假设
errback是“每次重试前的钩子”——它从不参与重试过程本身; - 若需在每次重试时记录日志或修改参数,应在
RetryMiddleware.process_exception()中实现,而非依赖errback; -
errback接收的是twisted.python.failure.Failure对象,需用failure.check()判断异常类型,不可直接isinstance(failure.value, XXX); - 自定义中间件若选择
return None(不处理异常),异常将继续向上传播,最终可能抵达errback;若return scrapy.Request(...),则原请求彻底退出,errback永不触发。
总之,errback 是 Scrapy 错误处理的“最后一道防线”,而非重试流程的组成部分。理解这一边界,才能合理设计容错逻辑:重试交给 RetryMiddleware 或自定义中间件,终局兜底与降级策略才由 errback 承担。










