简介
CrawlSpider是Spider类的派生类。它定义了一些规则(rule),爬虫根据规则爬取跟进Link。简而言之,它会根据规则提取出页面的link,进一步请求提取出的link。这样的机制,使得全站爬虫变得更加方便,代码更加简洁。
源码解析
Rule(规则)
Rule(link_extractor,
callback=None,
cb_kwargs=None,
follow=None,
process_links=None,
process_request=identity)
link_extractor:此参数为LinkExtractor对象,主要定义了一些爬取规则。
callback:指定回调函数。参数类型为字符串,即回调函数的名称,回调函数的参数为response。
cb_kwargs:指定传递给回调函数的参数,类型为字典。
follow:默认值为None。当值设置为True时,对根据规则提取出的link继续跟进。
process_links:进一步处理link_extractor提取的link。类型为String,根据参数调用同名的函数。
process_request:对request进一步处理。原码中给出了事例。
LinkExtractor
LinkExtractor(allow=(),
deny=(),
allow_domains=(),
deny_domains=(),
restrict_xpaths=(),
tags=('a', 'area'),
attrs=('href',),
canonicalize=False,
unique=True,
process_value=None,
deny_extensions=None,
restrict_css=(),
strip=True)
allow:根据正则表达式(或正则表达式列表)进行匹配,提取满足匹配规则的内容。若不指定值,则提取所有内容。
deny:根据正则表达式(或正则表达式列表)进行匹配,过滤满足匹配规则的内容。若不指定值,则不过滤任何内容。
allow_domains:允许提取的链接的域名。
deny_domains:不允许提取的链接的域名。
restrict_xpaths或restrict_css:根据xpath或css规则指定匹配规则的作用域名。
tags:指定要提取的链接的标签名。默认值为('a', 'area')。
attrs:指定要匹配的标签中的属性。默认值为('href',)。
canonicalize:是否标准化每个URL,默认值为False。
unique:是否过滤重复的链接。
process_value:进一步处理提取到的值。参数类型为字符串,即回调函数的名称。
deny_extensions: 指定需要忽略的url扩展名,如"bmp", "gif", "jpg", "mp3", "wav", "mp4", "wmv"。
strip:删除提取到的链接前后多余的空格。
CrawlSpider源码
class CrawlSpider(Spider):
rules = ()
def __init__(self, *a, **kw):
super(CrawlSpider, self).__init__(*a, **kw)
self._compile_rules()
"""
运行爬虫时默认调用parse方法,此方法不能重写
此方法调用_parse_response方法,parse_start_url作为回调函数
"""
def parse(self, response):
return self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True)
"""
若需要提取起始请求的response中的数据,可重写此方法
"""
def parse_start_url(self, response):
return []
"""
需要进一步处理结果时,可以重写此方法
"""
def process_results(self, response, results):
return results
"""
需要构造request时,可以重写此方法
"""
def _build_request(self, rule, link):
r = Request(url=link.url, callback=self._response_downloaded)
r.meta.update(rule=rule, link_text=link.text)
return r
"""
根据规则,跟进response中的link
"""
def _requests_to_follow(self, response):
if not isinstance(response, HtmlResponse):
return
seen = set()
for n, rule in enumerate(self._rules):
links = [lnk for lnk in rule.link_extractor.extract_links(response)
if lnk not in seen]
if links and rule.process_links:
links = rule.process_links(links)
for link in links:
seen.add(link)
r = self._build_request(n, link)
yield rule.process_request(r)
def _response_downloaded(self, response):
rule = self._rules[response.meta['rule']]
return self._parse_response(response, rule.callback, rule.cb_kwargs, rule.follow)
"""
调用callback处理response,并返回request或item
判断是否需要跟进link,若需要跟进,调用_requests_to_follow方法,并返回request或item
"""
def _parse_response(self, response, callback, cb_kwargs, follow=True):
#判断callback(回调函数)是否为None
if callback:
#回调函数不为空
#调用该方法,cb_kwargs为参数,cb_res为requests或item
cb_res = callback(response, **cb_kwargs) or ()
#调用process_results进一步处理cb_res
cb_res = self.process_results(response, cb_res)
#遍历cb_res,放回request或item
for requests_or_item in iterate_spider_output(cb_res):
yield requests_or_item
#判断是跟进link
if follow and self._follow_links:
#调用_requests_to_follow方法,并遍历返回的request或item
for request_or_item in self._requests_to_follow(response):
yield request_or_item
"""
将rule中的字符串转化为实际的方法
"""
def _compile_rules(self):
def get_method(method):
if callable(method):
return method
elif isinstance(method, six.string_types):
return getattr(self, method, None)
self._rules = [copy.copy(r) for r in self.rules]
for rule in self._rules:
rule.callback = get_method(rule.callback)
rule.process_links = get_method(rule.process_links)
rule.process_request = get_method(rule.process_request)
@classmethod
def from_crawler(cls, crawler, *args, **kwargs):
spider = super(CrawlSpider, cls).from_crawler(crawler, *args, **kwargs)
spider._follow_links = crawler.settings.getbool(
'CRAWLSPIDER_FOLLOW_LINKS', True)
return spider
def set_crawler(self, crawler):
super(CrawlSpider, self).set_crawler(crawler)
self._follow_links = crawler.settings.getbool('CRAWLSPIDER_FOLLOW_LINKS', True)
CrawlSpider项目的创建
scrapy genspider -t crawl 爬虫名 域名