一个 Request 带着 callback、errback、cb_kwargs、meta 去下载,回来以后哪一个会被调用、数据怎么传?这篇给 7 种不同结果的请求配上 callback 和 errback,再验证重定向、重试对 meta 的影响,以及 cb_kwargs 签名不匹配时的后果。
请求 谁被调用 信息
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。
/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=2cb_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 是同一个对象:Falseresponse.follow 不继承 meta 和 cb_kwargs,要自己传;Request.replace / copy 会连同 Scrapy 内部加的键一起复制。