bigywater.tech
Scrapy · 6

Request 的 callback、errback、cb_kwargs 与 meta

一个 Request 带着 callback、errback、cb_kwargs、meta 去下载,回来以后哪一个会被调用、数据怎么传?这篇给 7 种不同结果的请求配上 callback 和 errback,再验证重定向、重试对 meta 的影响,以及 cb_kwargs 签名不匹配时的后果。

404、500
都走 errback,Failure 里是 HttpError(带 response)
0 次
回调里抛异常时 errback 被调用的次数
0 个
response.follow 产生的新请求从原请求继承到的 meta 和 cb_kwargs 键数

errback 管什么

实测 · 本机执行输出
  请求                  谁被调用        信息
  200                 callback    status=200
  404                 errback     scrapy.spidermiddlewares.httperror.HttpError status=404
  500                 errback     scrapy.spidermiddlewares.httperror.HttpError status=500
  超时(服务器睡 3 s)        errback     scrapy.exceptions.DownloadTimeoutError
  连接被拒                errback     scrapy.exceptions.DownloadConnectionRefusedError
  DNS 失败              errback     scrapy.exceptions.CannotResolveHostError
  302→200             callback    status=200
  stats:downloader/exception_count=3;downloader/exception_type_count/*:{'scrapy.exceptions.CannotResolveHostError': 1, 'scrapy.exceptions.DownloadConnectionRefusedError': 1, 'scrapy.exceptions.DownloadTimeoutError': 1}

下载失败(超时、连接被拒、DNS 失败)和 HTTP 错误状态码(由 HttpErrorMiddleware 抛出的 HttpError)都走 errback。2.19 里下载异常是 scrapy.exceptions 下的 DownloadTimeoutError 等。

回调里的异常

实测 · 本机执行输出
  事件:['callback 开始']
  spider_exceptions/count=1;spider_exceptions/ValueError=1;日志:['[scrapy.core.scraper] ERROR: Spider error processing <GET http://127.0.0.1:9960/ok> (refer']

回调里抛出的异常不会进 errback,而是计入 spider_exceptions,日志 Spider error processing。

meta 与 cb_kwargs 传数据

实测 · 本机执行输出
  /ok:回调收到 a=1 b=[2],response.meta["m"]='x'
      response.meta 的全部键:['download_latency', 'download_slot', 'download_timeout', 'is_start_request', 'm', 'redirect_reasons', 'redirect_times', 'redirect_ttl', 'redirect_urls']
  /flaky:回调收到 a=1 b=[2],response.meta["m"]='x'
      response.meta 的全部键:['download_latency', 'download_slot', 'download_timeout', 'is_start_request', 'm', 'retry_times']
  重定向次数 redirect_count=1,重试 retry/count=2

cb_kwargs 作为回调的关键字参数传入,meta 通过 response.meta 访问。重定向和重试产生的新请求保留二者,并在 meta 里加上 redirect_* 或 retry_times。

签名要匹配

实测 · 本机执行输出
  缺少必需参数 b:spider_exceptions/count=1;TypeError: S.cb() missing 1 required positional argument: 'b'
  cb_kwargs 里多了回调不接受的 c:spider_exceptions/count=1;TypeError: S.cb() got an unexpected keyword argument 'c'
  回调有 **kwargs 兜底:spider_exceptions/count=0;回调正常执行,**kw 收到 {'c': 3}

回调的参数要和 cb_kwargs 的键一一对应,多一个少一个都是 TypeError;回调带 **kwargs 可以兜底。

哪些操作继承数据

实测 · 本机执行输出
  follow                   meta={}  cb_kwargs={}
  request.replace(url=…)   meta={'k': 'v', 'is_start_request': '…', 'download_timeout': '…', 'download_slot': '…', 'download_latency': '…'}  cb_kwargs={'c': 1}
  request.copy()           meta={'k': 'v', 'is_start_request': '…', 'download_timeout': '…', 'download_slot': '…', 'download_latency': '…'}  cb_kwargs={'c': 1}  与原 meta 是同一个对象:False

response.follow 不继承 meta 和 cb_kwargs,要自己传;Request.replace / copy 会连同 Scrapy 内部加的键一起复制。

分步演示

回调与 errback
本步的对象

    本文用到的工具与边界

    • 本机 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,数字每次运行会有小幅抖动。每个情形是一个单文件 spider;服务器按路径返回 200 / 404 / 500 / 睡 3 s / 302 / 前两次 503;连接被拒用本机的 127.0.0.1:1,DNS 失败用保留域名 .invalid
    • 没有验证:errback 在 spider 中间件 process_spider_input 抛其它异常时的行为;callback 是别的对象的方法时(JOBDIR 序列化,见后面的文章);meta 里放大对象或不可序列化对象的后果;cb_kwargs 和 meta 在 HTTP 缓存命中时的表现;async def 回调抛异常的统计
    京ICP备2026067502号-1京公网安备11010502063487号