扩展

Scrapy 的扩展系统是一个框架,它统一管理和配置扩展 Scrapy 核心功能的组件,例如中间件、扩展或管道。它为用户提供了 Scrapy 扩展管理的即插即用体验,并为开发者提供了广泛的配置控制。

激活和配置扩展

Crawler 初始化期间,启用的扩展列表从您的 ADDONS 设置中读取。

ADDONS 设置是一个字典,其中每个键都是一个扩展类或其导入路径,而值是其优先级。

这是一个示例,其中在项目的 settings.py 中启用了两个扩展。

ADDONS = {
    'path.to.someaddon': 0,
    SomeAddonClass: 1,
}

编写自己的扩展

扩展是包含以下方法的 Python 类

update_settings(settings)

此方法在Crawler 初始化期间调用。在这里,您应该执行依赖项检查(例如,对于外部 Python 库)并根据需要更新Settings 对象,例如为该扩展启用组件或设置其他扩展所需的配置。

参数:

settings (Settings) – 存储 Scrapy/组件配置的设置对象

它们还可以具有以下方法

classmethod from_crawler(cls, crawler)

如果存在,则调用此类方法以从Crawler 创建扩展实例。它必须返回扩展的新实例。crawler 对象提供了对所有 Scrapy 核心组件(如设置和信号)的访问;它是扩展访问它们并将功能挂钩到 Scrapy 的一种方式。

参数:

crawler (Crawler) – 使用此扩展的爬虫

扩展设置的设置应使用 addon 优先级(请参阅填充设置scrapy.settings.BaseSettings.set()

class MyAddon:
    def update_settings(self, settings):
        settings.set("DNSCACHE_ENABLED", True, "addon")

这允许用户在项目或爬虫配置中覆盖这些设置。对于可变对象(例如 ITEM_PIPELINES 的值是字典)的设置,这是不可能的。在这些情况下,您可以提供一个特定于扩展的设置来控制扩展是否会修改ITEM_PIPELINES

class MyAddon:
    def update_settings(self, settings):
        if settings.getbool("MYADDON_ENABLE_PIPELINE"):
            settings["ITEM_PIPELINES"]["path.to.mypipeline"] = 200

如果 update_settings 方法引发scrapy.exceptions.NotConfigured,则扩展将被跳过。这使得仅在满足某些条件时启用扩展变得容易。

回退

扩展提供的某些组件需要回退到“默认”实现,例如,自定义下载处理程序需要通过默认下载处理程序发送它无法处理的请求,或者包含一些额外处理但其他方面使用默认统计数据收集器的统计数据收集器。并且项目可能需要使用几种相同类型的自定义组件,例如两个支持不同类型的自定义请求的自定义下载处理程序,并且仍然需要对其他请求使用默认下载处理程序。为了使此类用例更容易配置,我们建议将这些自定义组件编写如下方式

  1. 自定义组件(例如 MyDownloadHandler)不应从默认的 Scrapy 组件(例如 scrapy.core.downloader.handlers.http.HTTPDownloadHandler)继承,而应该能够从特殊设置(例如 MY_FALLBACK_DOWNLOAD_HANDLER)加载回退组件的类,创建其实例并使用它。

  2. 包含这些组件的扩展应在其 update_settings() 方法中读取默认设置的当前值(例如 DOWNLOAD_HANDLERS),将该值保存到回退设置(前面提到的 MY_FALLBACK_DOWNLOAD_HANDLER)中,并将默认设置设置为扩展提供的组件(例如 MyDownloadHandler)。如果用户已设置回退设置,则不应更改它。

  3. 这样,如果有多个扩展想要修改相同的设置,它们都将回退到前一个扩展的组件,然后回退到 Scrapy 默认值。这取决于 ADDONS 设置中的优先级顺序。

扩展示例

设置一些基本配置

class MyAddon:
    def update_settings(self, settings):
        settings["ITEM_PIPELINES"]["path.to.mypipeline"] = 200
        settings.set("DNSCACHE_ENABLED", True, "addon")

检查依赖项

class MyAddon:
    def update_settings(self, settings):
        try:
            import boto
        except ImportError:
            raise NotConfigured("MyAddon requires the boto library")
        ...

访问爬虫实例

class MyAddon:
    def __init__(self, crawler) -> None:
        super().__init__()
        self.crawler = crawler

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler)

    def update_settings(self, settings): ...

使用回退组件

from scrapy.core.downloader.handlers.http import HTTPDownloadHandler
from scrapy.utils.misc import build_from_crawler


FALLBACK_SETTING = "MY_FALLBACK_DOWNLOAD_HANDLER"


class MyHandler:
    lazy = False

    def __init__(self, settings, crawler):
        dhcls = load_object(settings.get(FALLBACK_SETTING))
        self._fallback_handler = build_from_crawler(dhcls, crawler)

    def download_request(self, request, spider):
        if request.meta.get("my_params"):
            # handle the request
            ...
        else:
            return self._fallback_handler.download_request(request, spider)


class MyAddon:
    def update_settings(self, settings):
        if not settings.get(FALLBACK_SETTING):
            settings.set(
                FALLBACK_SETTING,
                settings.getwithbase("DOWNLOAD_HANDLERS")["https"],
                "addon",
            )
        settings["DOWNLOAD_HANDLERS"]["https"] = MyHandler