Spider 的起始请求怎么发出去?2.13 起 start() 取代了 start_requests(),2.19 里旧的方法已经不再被调用。这篇实测 start() 的默认行为、惰性消费的真相、await 与 yield item、起始请求的出队顺序和出错时的收尾。
start_urls = [/a, /a, /b](/a 重复):服务器收到 ['/a', '/a', '/b'],共 3 个请求 parse 看到的:a(is_start_request=True, dont_filter=True);b(is_start_request=True, dont_filter=True);a(is_start_request=True, dont_filter=True) dupefilter/filtered=0
Spider.start() 的默认实现遍历 start_urls,对每个 URL yield Request(url, dont_filter=True),所以重复的起始 URL 都会被请求。StartSpiderMiddleware 给这些请求的 meta 加上 is_start_request。
只定义 start_requests:服务器收到 0 个请求;start_requests 被调用:False;finish_reason=finished;response_received_count=0 日志里 WARNING / ERROR 行数:0(没有任何警告或提示,spider 安安静静地以 finished 结束)
2.19 的引擎只调用 start(),旧的 start_requests() 不再被调用,而且不会有任何提示。需要兼容老版本时两个方法都写。
直接 yield(300 个)
前 8 次 yield 时 已完成 [0, 0, 0, 0, 0, 0, 0, 0] / 调度器积压 [0, 0, 0, 0, 0, 1, 2, 3]
调度器积压的最大值 295;第 150 次 yield 在 163 ms,最后一次(第 300 次)在 173 ms;总耗时 8.1 s(理想 7.5 s);300 个响应
每次 yield 前 while engine.needs_backout(): await asyncio.sleep(0.01)(300 个)
前 8 次 yield 时 已完成 [0, 0, 0, 0, 4, 4, 4, 4] / 调度器积压 [0, 0, 0, 0, 0, 0, 0, 0]
调度器积压的最大值 0;第 150 次 yield 在 4296 ms,最后一次(第 300 次)在 8432 ms;总耗时 8.4 s(理想 7.5 s);300 个响应引擎循环里,下载器已满时直接继续取 start() 的下一个值,不等待。所以大量起始请求会一次性进入调度器。想按下载节奏出请求,在 start() 里自己检查 engine.needs_backout()。
158 ms yield i=0
161 ms parse p0
660 ms yield i=1
665 ms parse p1
1163 ms yield i=2
1166 ms parse p2
1666 ms yield i=3
1668 ms parse p3
2168 ms start() 结束
2168 ms spider_idle
finish_reason=finished(spider_idle 只在 start() 结束之后才出现)start() 是异步生成器,可以在 yield 之间 await(比如从外部队列取任务),已经 yield 的请求马上被处理;引擎在 start() 耗尽之前不会认为 spider 空闲。
pipeline 看到的 item:[{'from': 'start'}, {'from': 'parse'}];item_scraped_count=2;服务器收到 1 个请求start() 里 yield 的 item 不经过回调,直接进 pipeline。
默认设置:服务器收到的顺序 /a /b /a2 /b2 /b1 /a1 /c /c2 /c1 SCHEDULER_START_MEMORY_QUEUE 改成 LifoMemoryQueue:服务器收到的顺序 /a /c /a2 /c2 /c1 /a1 /b /b2 /b1
起始请求按 yield 顺序走 FIFO 队列,其它请求默认后进先出且优先于起始请求(同优先级时)。设置 SCHEDULER_START_MEMORY_QUEUE 可以改。
抛 RuntimeError:服务器收到 ['/first'];finish_reason=start_error;spider_exceptions/count=1;parse 被调用 1 次;日志:[scrapy.core.engine] ERROR: Error while reading start items and requests: boom i 抛 CloseSpider(reason="my_reason"):服务器收到 ['/first'];finish_reason=my_reason;spider_exceptions/count=0;parse 被调用 1 次
异常不会让已 yield 的请求作废。普通异常记 ERROR 并把 finish_reason 设为 start_error;CloseSpider 则使用你给的原因。