译者:OSGeo 中国

0.15 新版功能.

注解

这是一个新功能(在Scrapy0.15中引入),可能会受到小功能/API更新的影响。检查 release notes 收到更新通知。

测试 Spider 会变得特别烦人,虽然没有什么可以阻止你编写单元测试,但是任务会很快变得很麻烦。Scrapy提供了一种综合的方法,可以通过合同的方式测试你的 Spider 。

这允许您通过硬编码一个示例URL来测试 Spider 的每个回调,并检查回调如何处理响应的各种约束。每个合同的前缀都是 @ 并包含在docstring中。请参见以下示例:

  1. def parse(self, response):
  2. """ This function parses a sample response. Some contracts are mingled
  3. with this docstring.
  4. @url http://www.amazon.com/s?field-keywords=selfish+gene
  5. @returns items 1 16
  6. @returns requests 0 0
  7. @scrapes Title Author Year Price
  8. """

此回调使用三个内置合同进行测试:

  1. class scrapy.contracts.default.UrlContract

本合同(合同) @url )设置检查此spider的其他合同条件时使用的示例URL。本合同是强制性的。运行检查时将忽略缺少此协定的所有回调::

  1. @url url
  1. class scrapy.contracts.default.ReturnsContract

本合同(合同) @returns )为 Spider 返回的项和请求设置下限和上限。上限是可选的:

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

本合同(合同) @scrapes )检查回调返回的所有项是否具有指定的字段::

  1. @scrapes field_1 field_2 ...

使用 check 运行合同检查的命令。

定制合同

如果您发现您需要比内置的零碎合同更多的电力,您可以使用 SPIDER_CONTRACTS 设置:

  1. SPIDER_CONTRACTS = {
  2. 'myproject.contracts.ResponseCheck': 10,
  3. 'myproject.contracts.ItemValidate': 10,
  4. }

每个合同必须继承自 scrapy.contracts.Contract 可以覆盖三种方法:

  1. class scrapy.contracts.Contract(method, *args)

| 参数: |

  • method (function) — 与合同关联的回调函数
  • args (list) — 传入docstring的参数列表(空格分隔) | | | —- |
  1. adjust_request_args(args)

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

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

  1. pre_process(response)

这允许在将样本请求传递到回调之前,对从该请求接收的响应进行各种检查。

  1. post_process(output)

这允许处理回调的输出。迭代器在传递给这个Hook之前被转换为列表化的。

这里是一个演示合同,它检查在收到的响应中是否存在自定义头。提高 scrapy.exceptions.ContractFail 为了让故障得到完美的打印:

  1. from scrapy.contracts import Contract
  2. from scrapy.exceptions import ContractFail
  3. class HasHeaderContract(Contract):
  4. """ Demo contract which checks the presence of a custom header
  5. @has_header X-CustomHeader
  6. """
  7. name = 'has_header'
  8. def pre_process(self, response):
  9. for header in self.args:
  10. if header not in response.headers:
  11. raise ContractFail('X-CustomHeader not present')