蜘蛛契约

测试蜘蛛可能会变得特别烦人,虽然没有什么可以阻止你编写单元测试,但任务很快就会变得很麻烦。Scrapy 提供了一种集成的方式来通过契约测试你的蜘蛛。

这允许你通过硬编码一个示例 URL 来测试蜘蛛的每个回调,并检查回调如何处理响应的各种约束条件。每个契约都以 @ 为前缀,并包含在文档字符串中。请参阅以下示例

def parse(self, response):
    """
    This function parses a sample response. Some contracts are mingled
    with this docstring.

    @url http://www.example.com/s?field-keywords=selfish+gene
    @returns items 1 16
    @returns requests 0 0
    @scrapes Title Author Year Price
    """

您可以使用以下契约

class scrapy.contracts.default.UrlContract[source]

此契约(@url)设置了在检查此蜘蛛的其他契约条件时使用的示例 URL。此契约是强制性的。所有缺少此契约的回调在运行检查时都会被忽略。

@url url
class scrapy.contracts.default.CallbackKeywordArgumentsContract[source]

此契约(@cb_kwargs)设置示例请求的 cb_kwargs 属性。它必须是一个有效的 JSON 字典。

@cb_kwargs {"arg1": "value1", "arg2": "value2", ...}
class scrapy.contracts.default.MetadataContract[source]

此契约(@meta)设置示例请求的 meta 属性。它必须是一个有效的 JSON 字典。

@meta {"arg1": "value1", "arg2": "value2", ...}
class scrapy.contracts.default.ReturnsContract[source]

此契约(@returns)设置蜘蛛返回的项目和请求的下限和上限。上限是可选的。

@returns item(s)|request(s) [min [max]]
class scrapy.contracts.default.ScrapesContract[source]

此契约(@scrapes)检查回调返回的所有项目是否都具有指定的字段。

@scrapes field_1 field_2 ...

使用 check 命令运行契约检查。

自定义契约

如果您发现您需要比内置的 Scrapy 契约更强大的功能,您可以通过使用 SPIDER_CONTRACTS 设置在项目中创建和加载您自己的契约。

SPIDER_CONTRACTS = {
    "myproject.contracts.ResponseCheck": 10,
    "myproject.contracts.ItemValidate": 10,
}

每个契约都必须继承自 Contract,并且可以覆盖三个方法。

class scrapy.contracts.Contract(method, *args)[source]
参数:
  • method (collections.abc.Callable) – 与契约关联的回调函数。

  • args (list) – 传递到文档字符串中的参数列表(以空格分隔)。

adjust_request_args(args)[source]

这接收一个 dict 作为参数,其中包含请求对象的默认参数。默认情况下使用 Request,但这可以通过 request_cls 属性更改。如果链中的多个契约都定义了此属性,则使用最后一个。

必须返回相同或修改后的版本。

pre_process(response)

这允许在从示例请求接收到的响应上挂钩各种检查,然后将其传递给回调。

post_process(output)

这允许处理回调的输出。迭代器在传递到此挂钩之前会转换为列表。

如果期望不满足,则从 ContractFail 中引发 pre_processpost_process

class scrapy.exceptions.ContractFail[source]

如果契约失败,则引发此错误。

这是一个检查接收到的响应中是否存在自定义标头的演示契约。

from scrapy.contracts import Contract
from scrapy.exceptions import ContractFail


class HasHeaderContract(Contract):
    """
    Demo contract which checks the presence of a custom header
    @has_header X-CustomHeader
    """

    name = "has_header"

    def pre_process(self, response):
        for header in self.args:
            if header not in response.headers:
                raise ContractFail("X-CustomHeader not present")

检测检查运行

scrapy check 正在运行时,SCRAPY_CHECK 环境变量被设置为 true 字符串。您可以使用 os.environ 在使用 scrapy check 时对您的蜘蛛或设置进行任何更改。

import os
import scrapy


class ExampleSpider(scrapy.Spider):
    name = "example"

    def __init__(self):
        if os.environ.get("SCRAPY_CHECK"):
            pass  # Do some scraper adjustments when a check is running