【发布时间】:2016-04-28 14:26:51
【问题描述】:
最初的问题
我正在编写一个 CrawlSpider 类(使用 scrapy 库)并依靠大量 scrapy 异步魔法 使其工作。在这里,精简:
class MySpider(CrawlSpider):
rules = [Rule(LinkExtractor(allow='myregex'), callback='parse_page')]
# some other class attributes
def __init__(self, *args, **kwargs):
super(MySpider, self).__init__(*args, **kwargs)
self.response = None
self.loader = None
def parse_page_section(self):
soup = BeautifulSoup(self.response.body, 'lxml')
# Complicated scraping logic using BeautifulSoup
self.loader.add_value(mykey, myvalue)
# more methods parsing other sections of the page
# also using self.response and self.loader
def parse_page(self, response):
self.response = response
self.loader = ItemLoader(item=Item(), response=response)
self.parse_page_section()
# call other methods to collect more stuff
self.loader.load_item()
类属性rule 告诉我的蜘蛛跟随某些链接并在网页下载后跳转到回调函数。我的目标是在不运行爬虫甚至不发出真正的 HTTP 请求的情况下测试名为 parse_page_section 的解析方法。
我尝试了什么
本能地,我转向mock 图书馆。我理解你如何模拟一个函数来测试它是否被调用(使用哪些参数以及是否有任何副作用......),但这不是我想要的。我想实例化一个假对象 MySpider 并分配足够的属性以便能够在其上调用 parse_page_section 方法。
在上面的例子中,我需要一个response 对象来实例化我的ItemLoader,特别是一个self.response.body 属性来实例化我的BeautifulSoup。原则上,我可以制作这样的假对象:
from argparse import Namespace
my_spider = MySpider(CrawlSpider)
my_spider.response = NameSpace(body='<html>...</html>')
这适用于 BeautifulSoup 类,但我需要添加更多属性来创建 ItemLoader 对象。对于更复杂的情况,它会变得丑陋且难以管理。
我的问题
这是完全正确的方法吗?我在网上找不到类似的例子,所以我认为我的方法在更基本的层面上可能是错误的。任何见解将不胜感激。
【问题讨论】:
-
@ChrisP 感谢您的编辑。我没有将
scrapy标签放在首位,因为我认为这个问题通常与单元测试有关。 -
这绝对是一般的单元测试,但是做大量爬虫的人可能对单元测试爬虫有一些独特的见解。
-
在这种特殊的
CrawlSpider案例中,我可以假装响应对象。手工操作很困难,但这有帮助吗? requests-mock.readthedocs.io/en/latest/overview.html。这是一个好方法吗?
标签: python unit-testing mocking scrapy