
在 scrapy 中,errback 仅在请求最终失败且不再重试时被调用;若 retrymiddleware 捕获异常并返回新 request,则原请求的 errback 不会执行——它只对“终结性异常”生效。
在 scrapy 中,errback 仅在请求最终失败且不再重试时被调用;若 retrymiddleware 捕获异常并返回新 request,则原请求的 errback 不会执行——它只对“终结性异常”生效。
Scrapy 的请求生命周期中,errback 并非“每次异常都触发”,而是一个终态回调(final fallback):它仅在请求彻底退出下载流程、且无任何中间件(尤其是 RetryMiddleware)介入重试时才会被调用。
▶️ 正确理解 errback 的触发条件
根据 Scrapy 官方文档与源码逻辑:
- 当请求在下载阶段抛出异常(如
TimeoutError、DNSLookupError、ConnectionRefusedError等),该异常首先交由 Downloader Middleware 链处理; - 若
RetryMiddleware启用(默认开启),它会在process_exception()中检查异常类型是否在RETRY_HTTP_CODES或RETRY_EXCEPTIONS列表中(后者默认包含Twisted的TimeoutError,TCPTimedOutError,DNSLookupError等); - ✅ 若匹配成功:
RetryMiddleware.process_exception()返回一个新的Request(带retry_times+1 和更新的meta),Scrapy 立即中止当前请求流程,将新请求入队重试 → 原请求的errback被跳过,永不执行; - ❌ 若不匹配(如抛出未被
RETRY_EXCEPTIONS覆盖的冷门异常,或重试次数已达RETRY_TIMES上限),且无其他 middleware 处理该异常,则异常继续向上传播,最终触发Request.errback。
? 关键结论:
errback是“重试机制失效后的兜底回调”,而非“每次异常的即时响应”。
▶️ 实际代码验证示例
import scrapy
from twisted.internet.error import TimeoutError
class MySpider(scrapy.Spider):
name = 'timeout_demo'
def start_requests(self):
yield scrapy.Request(
url='https://httpbin.org/delay/10',
callback=self.parse,
errback=self.handle_failure, # ← 仅当重试耗尽后才调用
meta={'download_timeout': 2},
dont_filter=True
)
def parse(self, response):
self.logger.info(f"Success: {response.status}")
def handle_failure(self, failure):
self.logger.error(
f"ERRBACK TRIGGERED for {failure.request.url}: "
f"{failure.value.__class__.__name__} — {failure.getTraceback()}"
)
# 此处可手动重试(需谨慎避免无限循环)
if failure.check(TimeoutError):
self.logger.info("Manually retrying...")
yield failure.request.replace(dont_filter=True)⚠️ 注意:handle_failure 中 yield failure.request 是手动重试,与 RetryMiddleware 的自动重试互不干扰;但若 RetryMiddleware 已介入,该 errback 根本不会进入此函数。
▶️ 常见误区澄清
| 场景 |
errback 是否调用? |
说明 |
|---|---|---|
请求超时,RETRY_TIMES=2,第1次重试成功 |
❌ 否 | 原请求流程终止,errback 跳过 |
| 请求超时,连续3次均失败(达上限) | ✅ 是 |
RetryMiddleware 放弃重试,异常透传至 errback
|
process_request() 抛出 IgnoreRequest
|
✅ 是 |
RetryMiddleware 不处理该异常,直接走 errback 流程(文档明确说明) |
响应返回 403,且未配置 RETRY_HTTP_CODES 包含 403 |
❌ 否 |
403 是正常 HTTP 响应,不触发异常流,errback 不执行(应由 callback 处理) |
✅ 最佳实践建议
-
不要依赖
errback做常规重试:重试逻辑应优先交由RetryMiddleware配置(RETRY_HTTP_CODES,RETRY_EXCEPTIONS,RETRY_TIMES); -
errback适合做终态诊断与降级处理:如记录失败原因、切换代理池、写入死链日志、触发告警; -
若需动态重试策略(如封禁IP自动剔除):应编写自定义 Downloader Middleware,在
process_exception()中统一拦截、判断、替换代理并返回新Request,而非在errback中补救。
Scrapy 的设计哲学是“中间件驱动流程控制”,errback 是面向开发者的最后一道安全网,而非主干逻辑分支。理解其触发边界,是写出健壮、可维护爬虫的关键前提。

















