调度器

调度器组件从引擎接收请求,并将其存储到持久化和/或非持久化数据结构中。当引擎要求下载下一个请求时,它还会获取这些请求并将其反馈给引擎。

重写默认调度器

您可以通过在SCHEDULER设置中提供自定义调度器类的完整 Python 路径来使用它。

最小调度器接口

class scrapy.core.scheduler.BaseScheduler[source]

调度器组件负责存储从引擎接收到的请求,并根据请求将其(也)反馈给引擎。

所述请求的原始来源是

  • Spider: start 方法,为 start_urls 属性中的 URL 创建的请求,请求回调

  • Spider middleware: process_spider_outputprocess_spider_exception 方法

  • Downloader middleware: process_request, process_responseprocess_exception 方法

调度器返回其存储请求(通过 next_request 方法)的顺序在决定这些请求的下载顺序方面起着重要作用。参见 请求顺序

此课程中定义的方法构成了 Scrapy 引擎将与之交互的最小接口。

close(reason: str) Deferred[None] | None[source]

当引擎关闭爬虫时调用。它接收爬取完成的原因作为参数,可用于执行清理代码。

参数:

reason (str) – 描述爬虫关闭原因的字符串

abstractmethod enqueue_request(request: Request) bool[source]

处理引擎收到的请求。

如果请求正确存储,返回 True,否则返回 False

如果为 False,引擎将触发 request_dropped 信号,并且不会在以后再次尝试调度该请求。作为参考,当请求被去重过滤器拒绝时,默认的 Scrapy 调度器返回 False

classmethod from_crawler(crawler: Crawler) Self[source]

工厂方法,接收当前的 Crawler 对象作为参数。

abstractmethod has_pending_requests() bool[source]

如果调度器有已入队的请求,则为 True,否则为 False

abstractmethod next_request() Request | None[source]

返回下一个要处理的 Request,或者返回 None 以指示当前没有准备好的请求。

返回 None 意味着在当前 reactor 循环中,不会有任何来自调度器的请求发送给下载器。引擎将继续调用 next_request,直到 has_pending_requestsFalse

open(spider: Spider) Deferred[None] | None[source]

当引擎打开爬虫时调用。它接收爬虫实例作为参数,可用于执行初始化代码。

参数:

spider (Spider) – 当前爬取的爬虫对象

默认调度器

class scrapy.core.scheduler.Scheduler[source]

默认调度器。

请求存储在优先级队列(SCHEDULER_PRIORITY_QUEUE)中,这些队列根据 priority 对请求进行排序。

默认情况下,所有请求都使用基于内存的优先级队列。当使用 JOBDIR 时,也会创建基于磁盘的优先级队列,并且只有不可序列化的请求才存储在基于内存的优先级队列中。对于给定的优先级值,内存中的请求优先于磁盘中的请求。

每个优先级队列将请求存储在单独的内部队列中,每个优先级值一个队列。内存优先级队列使用 SCHEDULER_MEMORY_QUEUE 队列,而磁盘优先级队列使用 SCHEDULER_DISK_QUEUE 队列。当请求具有相同优先级时,内部队列决定请求顺序。默认情况下,起始请求存储在单独的内部队列中,并且排序方式不同

重复的请求通过 DUPEFILTER_CLASS 的实例进行过滤。

请求顺序

在默认设置下,待处理的请求存储在 LIFO 队列中(起始请求除外)。因此,爬取以 DFO 顺序进行,这通常是最方便的爬取顺序。但是,您可以强制使用 BFO自定义顺序除了最初的几个请求)。

起始请求顺序

起始请求按照它们从 start() 生成的顺序发送,并且在具有相同 priority 的情况下,其他请求优先于起始请求。

您可以将 SCHEDULER_START_MEMORY_QUEUESCHEDULER_START_DISK_QUEUE 设置为 None,以便在处理起始请求的顺序和优先级时与其他请求相同。

以 BFO 顺序爬取

如果您确实想以 BFO 顺序爬取,可以通过设置以下设置来实现

SCHEDULER_DISK_QUEUE = "scrapy.squeues.PickleFifoDiskQueue"
SCHEDULER_MEMORY_QUEUE = "scrapy.squeues.FifoMemoryQueue"

以自定义顺序爬取

您可以手动设置请求的 priority 以强制执行特定的请求顺序。

并发影响顺序

当待处理请求低于 CONCURRENT_REQUESTSCONCURRENT_REQUESTS_PER_DOMAIN 的配置值时,这些请求将并发发送。

因此,爬取的最初几个请求可能不会遵循预期的顺序。将这些设置降低到 1 会强制执行所需的顺序(除了第一个请求),但这会显著减慢整个爬取过程。

作业目录内容

警告

此类在 作业目录 中生成的文件是实现细节,在 Scrapy 的未来版本中可能会在没有警告的情况下发生变化。请勿将以下信息用于调试目的以外的任何用途。

当使用 JOBDIR 时,此调度器类

  • 作业目录 内创建一个名为 requests.queue 的目录,用于跟踪调度器中存储的所有请求(即尚未下载的请求)。

  • 在该目录内生成一个 active.json 文件,其中包含 SCHEDULER_PRIORITY_QUEUE 状态(startprios)的 JSON 表示。

    该文件在作业停止(干净地)时生成,并在恢复作业时加载。

  • 实例化配置的 SCHEDULER_PRIORITY_QUEUE,其中 requests.queue/ 作为持久化目录 (key),SCHEDULER_DISK_QUEUE 作为 downstream_queue_cls。优先级队列可以直接或通过 SCHEDULER_DISK_QUEUE 的实例在该目录内创建额外的文件和目录。

此调度器类还使用配置的 DUPEFILTER_CLASS,它也可能在作业目录内写入数据。

__init__(dupefilter: BaseDupeFilter, jobdir: str | None = None, dqclass: type[BaseQueue] | None = None, mqclass: type[BaseQueue] | None = None, logunser: bool = False, stats: StatsCollector | None = None, pqclass: type[ScrapyPriorityQueue] | None = None, crawler: Crawler | None = None)[source]

初始化调度器。

参数:
__len__() int[source]

返回已入队请求的总量

close(reason: str) Deferred[None] | None[source]
  1. 如果存在磁盘队列,将待处理的请求转储到磁盘

  2. 返回去重过滤器的 close 方法的结果

enqueue_request(request: Request) bool[source]

除非收到的请求被去重过滤器过滤掉,否则尝试将其推入磁盘队列,如果失败则回退到推入内存队列。

增加相应的统计信息,例如:scheduler/enqueuedscheduler/enqueued/diskscheduler/enqueued/memory

如果请求成功存储,返回 True,否则返回 False

classmethod from_crawler(crawler: Crawler) Self[source]

工厂方法,接收当前的 Crawler 对象作为参数。

has_pending_requests() bool[source]

如果调度器有已入队的请求,则为 True,否则为 False

next_request() Request | None[source]

从内存队列返回一个 Request 对象,如果内存队列为空,则回退到磁盘队列。如果没有更多已入队的请求,则返回 None

增加相应的统计信息,例如:scheduler/dequeuedscheduler/dequeued/diskscheduler/dequeued/memory

open(spider: Spider) Deferred[None] | None[source]
  1. 初始化内存队列

  2. 如果 jobdir 参数不为空,则初始化磁盘队列

  3. 返回去重过滤器的 open 方法的结果

优先级队列

class scrapy.pqueues.DownloaderAwarePriorityQueue(crawler: Crawler, downstream_queue_cls: type[QueueProtocol], key: str, slot_startprios: dict[str, Iterable[int]] | None = None, *, start_queue_cls: type[QueueProtocol] | None = None)[source]

此优先级队列考虑下载器活动:具有最少活跃下载量的域(槽位)会首先出队。

磁盘持久化

警告

此类在磁盘上生成的文件是实现细节,在 Scrapy 的未来版本中可能会在没有警告的情况下发生变化。请勿将以下信息用于调试目的以外的任何用途。

当组件使用非空 key 参数实例化此课程时,key 将用作持久化目录,并且在该目录内,此类为每个下载槽(域)创建一个子目录。

这些子目录以对应的下载槽命名,其中不安全的路径字符被下划线替换,并附加 MD5 散列后缀以避免冲突。

对于每个下载槽,此课程会创建一个 ScrapyPriorityQueue 实例,其中下载槽子目录作为 key,并使用其自身的 downstream_queue_cls

class scrapy.pqueues.ScrapyPriorityQueue(crawler: Crawler, downstream_queue_cls: type[QueueProtocol], key: str, startprios: Iterable[int] = (), *, start_queue_cls: type[QueueProtocol] | None = None)[source]

一个使用多个内部队列(通常是 FIFO 队列)实现的优先级队列。它为每个优先级值使用一个内部队列。内部队列必须实现以下方法:

  • push(obj)

  • pop()

  • close()

  • __len__()

可选地,队列可以提供一个 peek 方法,该方法应返回 pop 将返回的下一个对象,但不会将其从队列中移除。

ScrapyPriorityQueue 的 __init__ 方法接收一个 downstream_queue_cls 参数,该参数是一个类,用于在分配新优先级时实例化一个新的(内部)队列。

只应使用整数优先级。数字越小,优先级越高。

startprios 是一个起始优先级序列。如果队列之前关闭时留下了一些非空的优先级桶,则应在 startprios 中传递这些优先级。

磁盘持久化

警告

此类在磁盘上生成的文件是实现细节,在 Scrapy 的未来版本中可能会在没有警告的情况下发生变化。请勿将以下信息用于调试目的以外的任何用途。

当组件使用非空 key 参数实例化此课程时,key 将用作持久化目录。

对于每个入队的请求,此课程会检查

对于上述每个组合,此类都会创建一个 downstream_queue_cls(如果是起始请求且已传入 start_queue_cls,则创建 start_queue_cls)的实例,其中 key 设置为持久化目录的一个子目录,该子目录以请求优先级的负值命名(例如 -1),如果是一个起始请求,则带有 s 后缀(例如 -1s)。