在之前的文章我们通过 scrapy 框架 及 scrapy.Spider 类做了一个《糗事百科》的糗百爬虫,本章我们再来看一下相较于
scrapy.Spider 类更为强大的 CrawlSpider 类。

CrawlSpider 是Spider的派生类,Spider 类的设计原则是只爬取start_url列表中的网页,而 CrawlSpider
类定义了一些规则 (rule) 来提供跟进link的方便的机制,从爬取的网页中获取link并继续爬取的工作更适合。

源码参考
1 class CrawlSpider(Spider): 2 rules = () 3 def __init__(self, *a, **kw): 4
super(CrawlSpider, self).__init__(*a, **kw) 5 self._compile_rules() 6 7 #
首先调用parse()来处理start_urls中返回的response对象 8 #
parse()则将这些response对象传递给了_parse_response()函数处理,并设置回调函数为parse_start_url() 9 #
设置了跟进标志位True 10 #parse将返回item和跟进了的Request对象 11 def parse(self, response): 12
return self._parse_response(response, self.parse_start_url, cb_kwargs={},
follow=True) 13 14 #处理start_url中返回的response,需要重写 15 def parse_start_url(self,
response):16 return [] 17 18 def process_results(self, response, results): 19
return results 20 21 #从response中抽取符合任一用户定义'规则'的链接,并构造成Resquest对象返回 22 def
_requests_to_follow(self, response):23 if not isinstance(response,
HtmlResponse):24 return 25 seen = set() 26 #抽取之内的所有链接,只要通过任意一个'规则',即表示合法 27 for
n, rulein enumerate(self._rules): 28 links = [l for l in
rule.link_extractor.extract_links(response)if l not in seen] 29 #
使用用户指定的process_links处理每个连接 30 if links and rule.process_links: 31 links =
rule.process_links(links)32 #
将链接加入seen集合,为每个链接生成Request对象,并设置回调函数为_repsonse_downloaded() 33 for link in
links:34 seen.add(link) 35 #构造Request对象,并将Rule规则中定义的回调函数作为这个Request对象的回调函数 36
r = Request(url=link.url, callback=self._response_downloaded) 37
r.meta.update(rule=n, link_text=link.text) 38 #
对每个Request调用process_request()函数。该函数默认为indentify,即不做任何处理,直接返回该Request. 39 yield
rule.process_request(r)40 41 #处理通过rule提取出的连接,并返回item以及request 42 def
_response_downloaded(self, response):43 rule = self._rules[response.meta['rule'
]]44 return self._parse_response(response, rule.callback, rule.cb_kwargs,
rule.follow)45 46 #解析response对象,会用callback解析处理他,并返回request或Item对象 47 def
_parse_response(self, response, callback, cb_kwargs, follow=True): 48 #
首先判断是否设置了回调函数。(该回调函数可能是rule中的解析函数,也可能是 parse_start_url函数) 49 #
如果设置了回调函数(parse_start_url()),那么首先用parse_start_url()处理response对象, 50 #
然后再交给process_results处理。返回cb_res的一个列表 51 if callback: 52 #
如果是parse调用的,则会解析成Request对象 53 #如果是rule callback,则会解析成Item 54 cb_res =
callback(response, **cb_kwargs)or () 55 cb_res = self.process_results(response,
cb_res)56 for requests_or_item in iterate_spider_output(cb_res): 57 yield
requests_or_item58 59 #如果需要跟进,那么使用定义的Rule规则提取并返回这些Request对象 60 if follow and
self._follow_links:61 #返回每个Request对象 62 for request_or_item in
self._requests_to_follow(response):63 yield request_or_item 64 65 def
_compile_rules(self):66 def get_method(method): 67 if callable(method): 68
return method 69 elif isinstance(method, basestring): 70 return getattr(self,
method, None)71 72 self._rules = [copy.copy(r) for r in self.rules] 73 for rule
in self._rules: 74 rule.callback = get_method(rule.callback) 75
rule.process_links = get_method(rule.process_links) 76 rule.process_request =
get_method(rule.process_request)77 78 def set_crawler(self, crawler): 79
super(CrawlSpider, self).set_crawler(crawler)80 self._follow_links =
crawler.settings.getbool('CRAWLSPIDER_FOLLOW_LINKS', True)
CrawlSpider继承于Spider类,除了继承过来的属性外(name、allow_domains),还提供了新的属性和方法:

LinkExtractors

 from scrapy.linkextractors import LinkExtractor 

Link Extractors 的目的很简单: 提取链接。

每个LinkExtractor有唯一的公共方法是 extract_links(),它接收一个 Response 对象,并返回一个
scrapy.link.Link 对象。

Link Extractors要实例化一次,并且 extract_links 方法会根据不同的 response 调用多次提取链接。
1 class scrapy.linkextractors.LinkExtractor( 2 allow = (), 3 deny = (), 4
allow_domains = (), 5 deny_domains = (), 6 deny_extensions = None, 7
restrict_xpaths = (), 8 tags = ('a','area'), 9 attrs = ('href'), 10
canonicalize = True, 11 unique = True, 12 process_value = None 13 )
主要参数:

*
allow:满足括号中“正则表达式”的值会被提取,如果为空,则全部匹配。

*
deny:与这个正则表达式(或正则表达式列表)不匹配的URL一定不提取。

*
allow_domains:会被提取的链接的domains。

*
deny_domains:一定不会被提取链接的domains。

*
restrict_xpaths:使用xpath表达式,和allow共同作用过滤链接。

rules


在rules中包含一个或多个Rule对象,每个Rule对爬取网站的动作定义了特定操作。如果多个rule匹配了相同的链接,则根据规则在本集合中被定义的顺序,第一个会被使用。
1 class scrapy.spiders.Rule( 2 link_extractor, 3 callback = None, 4 cb_kwargs
= None, 5 follow = None, 6 process_links = None, 7 process_request = None 8 )
*
link_extractor:是一个Link Extractor对象,用于定义需要提取的链接。

*
callback: 从link_extractor中每获取到链接时,参数所指定的值作为回调函数,该回调函数接受一个response作为其第一个参数。

注意:当编写爬虫规则时,避免使用parse作为回调函数。由于CrawlSpider使用parse方法来实现其逻辑,如果覆盖了 parse方法,crawl
spider将会运行失败。

*
follow:是一个布尔(boolean)值,指定了根据该规则从response提取的链接是否需要跟进。 如果callback为None,follow
默认设置为True ,否则默认为False。

*
process_links:指定该spider中哪个的函数将会被调用,从link_extractor中获取到链接列表时将会调用该函数。该方法主要用来过滤。

*
process_request:指定该spider中哪个的函数将会被调用, 该规则提取到每个request时都会调用该函数。 (用来过滤request)

 

接下来我们就按上面所说的内容将之前的糗百爬虫做一下修改,我们将 qiubaiSpider.py 的代码改为如下:
1 import scrapy 2 # 导入CrawlSpider类和Rule 3 from scrapy.spiders import
CrawlSpider, Rule 4 # 导入链接规则匹配类,用来提取符合规则的连接 5 from scrapy.linkextractors import
LinkExtractor 6 from ..items import QiushiItem 7 8 9 class
QiushiSpider(CrawlSpider):10 # 爬虫名 11 name = "qiubai" 12 # 允许爬虫作用的范围,不能越界 13
allowd_domains = ["https://www.qiushibaike.com/"] 14 # 爬虫起始url 15 start_urls = [
"https://www.qiushibaike.com/text/page/1/"] 16 # Response
里链接的提取规则,返回的符合匹配规则的链接匹配对象的列表 17 pageLink = LinkExtractor(allow=("/page/\d+")) 18
# 获取这个列表里的链接,依次发送请求,并且继续跟进,调用指定回调函数处理 19 rules = [ 20 Rule(pageLink, callback="
parseContent", follow=True) 21 ] 22 23 # 指定的回调函数 24 def parseContent(self,
response):25 # 通过 scrayy 自带的 xpath 匹配想要的信息 26 qiushi_list = response.xpath('
//div[contains(@id,"qiushi_tag")]') 27 for site in qiushi_list: 28 # 实例化从
items.py 导入的 QiushiItem 类 29 item = QiushiItem() 30 # 根据查询发现匿名用户和非匿名用户的标签不一样 31
try: 32 # 非匿名用户 33 username = site.xpath('./div/a/img/@alt')[0].extract() # 作者
34 imgUrl = site.xpath('./div/a/img/@src')[0].extract() # 头像 35 except
Exception:36 # 匿名用户 37 username = site.xpath('./div/span/img/@alt')[0].extract()
# 作者 38 imgUrl = site.xpath('./div/span/img/@src')[0].extract() # 头像 39 content
= site.xpath('.//div[@class="content"]/span[1]/text()').extract() 40 item['
username'] = username 41 item['imgUrl'] = "https:" + imgUrl 42 item['content'] =
content43 44 # 将获取的数据交给 pipeline 管道文件 45 yield item
在控制台或终端输入  scrapy crawl qiubai  即可运行程序并获取糗百数据。

需要注意的是在 rule 规则中的 callback 千万不能写 parse,因为 CrawlSpider 使用 parse 方法来实现其逻辑,如果覆盖了
parse方法,crawl spider将会运行失败。

 

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信