附加组件

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

激活和配置附加组件

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

ADDONS 设置是一个字典,其中每个键是一个附加组件类或其导入路径,值是其优先级。

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

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

编写你自己的附加组件

附加组件是包含以下方法的Python类:

update_settings(settings)

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

Parameters:

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

他们还可以有以下方法:

classmethod from_crawler(cls, crawler)

如果存在,这个类方法被调用来从Crawler创建一个附加组件实例。它必须返回附加组件的新实例。爬虫对象提供了对Scrapy所有核心组件的访问,如设置和信号;这是附加组件访问它们并将其功能集成到Scrapy中的一种方式。

Parameters:

爬虫 (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