插件

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

激活和配置插件

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

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

这是一个在项目 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() 方法来更新在 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,则会跳过该插件。这使得仅在满足某些条件时启用插件变得容易。

回退机制

插件提供的一些组件需要回退到“默认”实现,例如,一个自定义下载处理器需要通过默认下载处理器发送它不处理的请求,或者一个统计收集器包含一些额外处理但 otherwise 使用默认统计收集器。而且,一个项目可能需要使用同类型的多个自定义组件,例如两个支持不同类型自定义请求的自定义下载处理器,并且仍然需要对其他请求使用默认下载处理器。为了使此类用例更易于配置,我们建议以以下方式编写此类自定义组件

  1. 自定义组件(例如 MyDownloadHandler)不应该继承自默认的 Scrapy 组件(例如 scrapy.core.downloader.handlers.http11.HTTP11DownloadHandler),而是应该能够从一个特殊设置(例如 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.utils.misc import build_from_crawler, load_object


FALLBACK_SETTING = "MY_FALLBACK_DOWNLOAD_HANDLER"


class MyHandler:
    lazy = False

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

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

    async def close(self):
        pass


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