附加组件

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

激活和配置附加组件

Crawler 初始化期间,已启用的附加组件列表会从您的 ADDONS 设置中读取。

The ADDONS setting is a dict in which every key is an add-on class or its import path and the value is its priority.

这是一个在项目 settings.py 中启用两个附加组件的示例

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

编写自己的附加组件

附加组件是包含以下一个或两个方法的组件

update_settings(settings)

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

参数:

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

classmethod update_pre_crawler_settings(cls, settings)

使用此类方法代替 update_settings() 方法来更新 pre-crawler settings,其值在创建 Crawler 对象之前使用。

参数:

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

附加组件设置的配置应该使用 addon 优先级(参见 填充设置scrapy.settings.BaseSettings.set()

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

这允许用户在项目或爬虫配置中覆盖这些设置。

当编辑某个设置的值而不是完全覆盖它时,通常最好保持其优先级不变。例如,编辑组件优先级字典时。

如果 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 设置中的优先级顺序。

附加组件示例

设置一些基本配置

from myproject.pipelines import MyPipeline


class MyAddon:
    def update_settings(self, settings):
        settings.set("DNSCACHE_ENABLED", True, "addon")
        settings.remove_from_list("METAREFRESH_IGNORE_TAGS", "noscript")
        settings.setdefault_in_component_priority_dict(
            "ITEM_PIPELINES", MyPipeline, 200
        )

提示

编辑组件优先级字典设置(例如 ITEM_PIPELINES)时,考虑使用设置方法如 replace_in_component_priority_dict()set_in_component_priority_dict()setdefault_in_component_priority_dict() 以避免错误。

检查依赖

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