bigywater.tech
Scrapy · 42

errback 与 Failure:下载失败时拿到什么

请求失败时 errback 拿到的 Failure 里到底是什么?不同故障对应哪个异常类?怎么在 errback 里判断类型?errback 的返回值又会被怎么处理?这篇用 11 种故障逐个造出来。

11 种
造出来的下载故障数,各对应不同的 failure.type
DownloadFailedError
包着 Twisted/OpenSSL 底层异常的统一外壳(连接被关闭、非 HTTP 回包、TLS 失败)
0 条日志
errback return None 时的日志

异常类的分工

实测 · 本机执行输出
  服务器返回 500                           scrapy.spidermiddlewares.httperror.HttpError    带 response Ignoring non-200 response
  服务器返回 404                           scrapy.spidermiddlewares.httperror.HttpError    带 response Ignoring non-200 response
  连接被拒(127.0.0.1:1)                   scrapy.exceptions.DownloadConnectionRefusedError无 response Connection was refused by other side: 61: Connection refused.
  DNS 解析失败(.invalid)                  scrapy.exceptions.CannotResolveHostError        无 response DNS lookup failed: no results for hostname lookup: no-such-host.invalid.
  读取超时(服务器睡 2 s,超时 1 s)               scrapy.exceptions.DownloadTimeoutError          无 response Getting http://127.0.0.1:9964/x took longer than 1.0 seconds.
  收到请求后直接关闭连接                         scrapy.exceptions.DownloadFailedError           无 response [<twisted.python.failure.Failure twisted.internet.error.ConnectionDone: Connection was clo
  声明 Content-Length: 100 只发 15 字节就关闭  scrapy.exceptions.ResponseDataLossError         无 response (空消息)
  分块编码的块大小不是十六进制                      scrapy.exceptions.ResponseDataLossError         无 response (空消息)
  回的根本不是 HTTP                         scrapy.exceptions.DownloadFailedError           无 response [<twisted.python.failure.Failure twisted.web._newclient.ParseError: ('non-integer status c
  正文 5000 字节,DOWNLOAD_MAXSIZE=1000    scrapy.exceptions.DownloadCancelledError        无 response Expected to receive 5000 bytes which is larger than download max size (1000) in request <G
  https 请求打到明文 http 端口                scrapy.exceptions.DownloadFailedError           无 response [<twisted.python.failure.Failure OpenSSL.SSL.Error: [('SSL routines', '', 'wrong version n
  URL 缺少协议(Request("bar")):构造时就抛 ValueError: Missing scheme in request url: bar

状态码错误是 HttpError(带 response);网络层有专门的连接被拒、DNS、超时类;数据截断、超限也各有一个;其它底层问题统一包成 DownloadFailedError,原始异常在消息和 __cause__ 里。

Failure API

实测 · 本机执行输出
  500  check(HttpError)=True;check(DownloadConnectionRefusedError)=False;check(两者)→HttpError;failure.value.response.status=500
       getErrorMessage()='Ignoring non-200 response';getTraceback() 共 8 行,最后一行 'scrapy.spidermiddlewares.httperror.HttpError: Ignoring non-200 response';value.__cause__ 的类型 NoneType
  x    check(HttpError)=False;check(DownloadConnectionRefusedError)=True;check(两者)→DownloadConnectionRefusedError;failure.value.response.status=None
       getErrorMessage()='Connection was refused by other side: 61: Connection refused';getTraceback() 共 38 行,最后一行 'scrapy.exceptions.DownloadConnectionRefusedError: Connection was refused by othe';value.__cause__ 的类型 ConnectionRefusedError

failure.check(*classes) 返回命中的类用来分支,failure.request 是出错的请求,failure.value.response 只有 HttpError 才有。

返回值

实测 · 本机执行输出
  return None(什么都不做)        事件 [];ERROR 日志 [];stats downloader/exception_count=1, spider_exceptions=0
  yield 一个 item             事件 [{'who': 'item', 'item': {'failed': 'http://127.0.0.1:1/x'}}];ERROR 日志 [];stats downloader/exception_count=1, spider_exceptions=0
  返回一个新 Request(换个地址)       事件 [{'who': 'retry callback', 'url': 'http://127.0.0.1:9969/ok'}];ERROR 日志 [];stats downloader/exception_count=1, spider_exceptions=0
  不定义 errback               事件 [];ERROR 日志 ['ERROR: Error downloading <GET http://127.0.0.1:1/x>'];stats downloader/exception_count=1, spider_exceptions=0
  errback 自己抛异常             事件 [];ERROR 日志 ['ERROR: Error downloading <GET http://127.0.0.1:1/x>'];stats downloader/exception_count=1, spider_exceptions=1

errback 的返回值按回调规则处理:item 进 pipeline,Request 被调度,None 吞掉;没有 errback 或 errback 抛异常时 Scrapy 记 ERROR。

分步演示

下载失败
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Scrapy 2.19.0 / Twisted 26.4.0 / lxml 6.1.3 / parsel 1.12.1(临时虚拟环境);所有请求只发给本机自己起的测试服务器(127.0.0.1,端口 9960 以上,不访问任何真实网站);服务器可以按实验需要制造延迟、重定向、压缩、断连等行为,并记录收到的原始请求;每个实验在临时目录里新建 Scrapy 项目或单文件 spider,命令输出来自真实的 scrapy 进程;计时用 time.perf_counter,数字每次运行会有小幅抖动。故障由原始套接字服务器制造(直接关闭、截断、分块错误、非 HTTP 回包、大正文、睡眠)或指向无效地址;RETRY_ENABLED = 0,DOWNLOAD_TIMEOUT = 1;每种故障一个单文件 spider,errback 里记录 failure 的类型和消息
    • 没有验证:重试开启时 errback 看到的是最后一次失败(上一批文章里重试一篇已测);HTTP/2 处理器和 aiohttp 处理器下的异常类是否相同;代理失败(TunnelError)的 Failure;ssl 证书校验失败(自签名证书)的具体异常;failure.value 的其它属性
    京ICP备2026067502号-1京公网安备11010502063487号