Scrapy爬虫之CrawlSpider

简介

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 爬虫名 域名

猜你喜欢

转载自blog.csdn.net/hengdawei3087/article/details/87890146