在 Scrapy 2.19 上给引擎的每一层挂钩子,记录一个请求的完整路径(29 个事件);默认队列是后进先出、不是严格深度优先;CONCURRENT_REQUESTS 从 1 到 24 总耗时 7.34 s 降到 0.34 s;回调返回字符串和整数也被当成 item;DontCloseSpider 之后 spider_idle 每约 5 秒重复一次。
在 Scrapy 2.19 上对比三种运行方式:默认 asyncio reactor、SelectReactor、TWISTED_REACTOR_ENABLED=False(无 reactor、aiohttp 下载);单线程并发 100 个慢请求只要 0.6 s;回调里 time.sleep(0.1) 让 20 个请求从 0.34 s 变成 2.39 s;SelectReactor 下 async 回调 await asyncio.sleep 全部抛 RuntimeError。
实测 Scrapy 2.19 的四个启动类:start() 只能调一次(第二次 ReactorNotRestartable);CrawlerProcess 并发两个 spider 0.62 s、CrawlerRunner 顺序 1.39 s;CrawlerRunner 不 stop reactor 进程永远不退出;asyncio.run 配 asyncio reactor 会卡死;第一次 SIGINT 优雅停止、第二次强制退出。
实测 Scrapy 2.19 的 settings 优先级:default 0 / command 10 / addon 15 / project 20 / spider 30 / cmdline 40;startproject 模板默认 DOWNLOAD_DELAY = 1;spider.update_settings 里用 "cmdline" 优先级输给 -s,要用 41 才赢;字典型设置整体替换;-s 的值是字符串('False' 当 if 是真);设置在 start() 里已冻结。
实测 Scrapy 2.19 的 start():默认 start() 对 start_urls 的重复项也会请求(dont_filter=True);只写旧的 start_requests 的 spider 什么也不抓、没有任何警告;start() 一次性把 300 个请求塞进调度器(积压 295),用 needs_backout 退让后积压为 0;start 请求走独立的 FIFO 队列,其它请求优先。
实测 Scrapy 2.19 的 callback / errback / cb_kwargs / meta:404、500 走 errback(HttpError),超时、连接被拒、DNS 失败也走 errback(新的 Download…Error 异常类);回调里抛异常不触发 errback;cb_kwargs 与 meta 在重定向和重试后保留;response.follow 什么都不继承。
实测 Scrapy 2.19 怎么决定响应编码:构造参数 > BOM > HTTP 头 > <meta> > 自动探测;头写错时不报错但满是替换字符,JSON 的键会因此合并丢数据;短 GBK 文本被自动探测成 cp949;gb2312 → gb18030、latin1 → cp1252;只有文本类型的响应才有 .text。
实测 Scrapy 2.19 的调度队列:有 JOBDIR 时 21 个请求全进磁盘队列;priority 大的先出、同优先级默认后进先出;不可序列化的请求退回内存队列(默认无日志)并因“内存先于磁盘”打乱优先级;默认的 DownloaderAwarePriorityQueue 让优先级只在域名内有效,且与 CONCURRENT_REQUESTS_PER_IP 冲突。
实测 Scrapy 2.19 的请求去重:参数顺序、#片段、主机大小写算同一个,:80、末尾 /、http/https、www. 算不同;POST 体逐字节比较、请求头和 meta 默认不参与;requests.seen 每个指纹 22 字节;自己写的 start() 默认不带 dont_filter;100 万个指纹的 set 占 86.6 MB。
实测 dont_filter 何时该用:有环的站点全部 dont_filter=True 会一直转(1 秒 799 个请求),要靠 DEPTH_LIMIT / CLOSESPIDER_* 刹车;重试请求自带 True;重定向沿用原请求的值,重定向到已抓过的 URL 会被去重吃掉,回调不被调用;轮询同一个 URL 必须加。
实测下载器中间件的调用顺序:process_request 按数字从小到大、process_response 反过来;595 看到 gzip 的 64 字节、585 看到解压后的 2613 字节;process_request 返回 Response 时仍然调用全部 process_response;返回 Request 会重新走一遍中间件链;process_exception 按响应方向调用。
实测 Scrapy 2.19 爬虫中间件:input 从小到大、output 从大到小;input 抛异常直接进 errback 且不算 spider 异常;output 可以过滤请求和 item;process_spider_exception 按 C、B、A 调用,返回可迭代对象即可终止;同步版 process_spider_output 在 2.19 启动即 TypeError。