Scrapy Contracts问题
我开始研究scrapy框架。也实施了一些蜘蛛 提取,但我不能为蜘蛛写一个单元测试用例,因为合同 scrapy提供的包文档没有正确的编写程序 测试用例。请帮帮我。
答案 0 :(得分:11)
是的,Spiders Contracts远非清晰明确。
我不是编写蜘蛛合同的专家(实际上只在web-scraping tutorial newcoder.io处理suggested here时写了一次。但每当我需要为Scrapy蜘蛛编写测试时,我更倾向于遵循方法Scrapy Unit Testing - 从本地html文件创建一个虚假的响应。如果这仍然是一个单元测试程序,这是有争议的,但这为您提供了更多的灵活性和稳健性。
请注意,您仍然可以编写合同,但很快就会感到需要扩展它们并编写自定义合同。这几乎没问题。
相关链接:
答案 1 :(得分:2)
测试蜘蛛的两个最基本的问题可能是:
Scrapy提供了一种测试蜘蛛的方法:合同。
合同看起来有些不可思议。他们生活在多行文档字符串中。合同“语法”为:@contract_name <arg>
。您可以创建自己的合同,这很简洁。
要使用合同,请在合同名称前加上@
。合同的名称由给定合同子类上的.name
属性指定。这些合同子类可以是内置的,也可以是您创建的自定义子类。
最后,上述文档字符串必须存在于您的Spider的回调中。这是parse
回调中存在的一些基本合约的示例;默认回调。
def parse(self, response):
"""This function gathers the author and the quote text.
@url http://quotes.toscrape.com/
@returns items 1 8
@returns requests 0 0
@scrapes author quote_text
"""
您可以通过scrapy check
运行此合同;或者,使用scrapy check -l
列出您的合同。
以上合同已使用三个内置合同进行了测试:
scrapy.contracts.default.UrlContract
scrapy.contracts.default.ReturnsContract
scrapy.contracts.default.ScrapesContract
UrlContract
是强制性的,并不是真正的合同,因为它不用于验证。 @url
协定用于设置通过scrapy check
测试蜘蛛时蜘蛛将爬网的URL。在这种情况下,我们指定http://quotes.toscrape.com/
。但是我们可以指定http://127.0.0.1:8080/home-11-05-2019-1720.html
,这是我用quotes.toscrape.com
命令保存的scrapy view http://quotes.toscrape.com/
的本地版本。
ReturnsContract
用于检查您正在测试的回调的输出。如您所见,该合同被两次调用,具有不同的参数。但是,您不能只在其中放置任何arg。引擎盖下有一个预期的args字典:
objects = {
'request': Request,
'requests': Request,
'item': (BaseItem, dict),
'items': (BaseItem, dict),
}
我们的合同认为我们的蜘蛛@returns items 1 16
。 1
和16
是上下限。上限是可选的;如果未指定?,则将其设置为无穷大。
try:
self.max_bound = int(self.args[2])
except IndexError:
self.max_bound = float('inf')
但是,@returns
可以帮助您了解蜘蛛是否返回预期的物品或请求量。
最后,@scrapes
合同是最后一个内置合同。用于检查已刮除项目中字段的存在。它只是遍历回调的输出字典,并构造缺少的属性的列表:
class ScrapesContract(Contract):
""" Contract to check presence of fields in scraped items
@scrapes page_name page_body
"""
name = 'scrapes'
def post_process(self, output):
for x in output:
if isinstance(x, (BaseItem, dict)):
missing = [arg for arg in self.args if arg not in x]
if missing:
raise ContractFail(
"Missing fields: %s" % ", ".join(missing))
运行:scrapy check
如果一切顺利,您会看到:
...
----------------------------------------------------------------------
Ran 3 contracts in 0.140s
OK
如果发生爆炸,您会看到:
F..
======================================================================
FAIL: [example] parse (@returns post-hook)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/adnauseum/.virtualenvs/scrapy_testing-CfFR3tdG/lib/python3.7/site-packages/scrapy/contracts/__init__.py", line 151, in wrapper
self.post_process(output)
File "/Users/adnauseum/.virtualenvs/scrapy_testing-CfFR3tdG/lib/python3.7/site-packages/scrapy/contracts/default.py", line 90, in post_process
(occurrences, self.obj_name, expected))
scrapy.exceptions.ContractFail: Returned 10 items, expected 0
----------------------------------------------------------------------
假设您要签订@has_header X-CustomHeader
合同。这将确保您的蜘蛛检查X-CustomHeader
是否存在。 Scrapy合同只是具有三个可覆盖方法的类:adjust_request_args
,pre_process
和post_process
。从那里,只要没有达到期望,您就需要从ContractFail
或pre_process
筹集post_process
。
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' # add the command name to the registry
def pre_process(self, response):
for header in self.args:
if header not in response.headers:
raise ContractFail(f"{header} not present")
看来合同可以帮助您了解两件事:
您所做的代码更改没有破坏
Site-Page-07-14-2019.html
。您可以通过运行scrapy view <url>
保存这些页面。 Scrapy将在浏览器中打开此页面,但还将保存您需要的所有内容的HMTL文件。您要抓取的页面没有更改(以影响您的方式)
尽管合同很有用,但您可能必须做更多的工作才能确保您的蜘蛛。例如,不能保证您要抓取的项目数量始终保持恒定。在这种情况下,您可以考虑对模拟服务器进行爬网并针对收集的项目运行测试。似乎缺少文档和最佳实践。
最后,由Spidermon的Scrapinghub制作了一个项目,该项目可用于监视运行中的蜘蛛:https://spidermon.readthedocs.io/en/latest/getting-started.html
您可以根据模型定义验证抓取的物品,并获得蜘蛛的统计信息(当前抓取的num个物品,不符合验证的num个物品等)。