Scrapy学习——CrawlSpider详解

  首先,说是详解,其实也并不是多么深入,只是自己的一些学习笔记。其次,本文适合一边翻源码,一边阅读。

  从CrawlSpider的源码(crawl.py)中我们可以看到,CrawlSpider是继承Spider类的。在scrapy的官方文档中对Spider的描述如下:

  1. 以初始的URL初始化Request,并设置回调函数。 当该request下载完毕并返回时,将生成response,并作为参数传给该回调函数。
    spider中初始的request是通过调用 start_requests() 来获取的。 start_requests() 读取 start_urls 中的URL, 并以 parse 为回调函数生成 Request 。

  2. 在回调函数内分析返回的(网页)内容,返回 Item 对象或者 Request 或者一个包括二者的可迭代容器。 返回的Request对象之后会经过Scrapy处理,下载相应的内容,并调用设置的callback函数(函数可相同)。

  3. 在回调函数内,您可以使用 选择器(Selectors) (您也可以使用BeautifulSoup, lxml 或者您想用的任何解析器) 来分析网页内容,并根据分析的数据生成item。

  4. 最后,由spider返回的item将被存到数据库(由某些 Item Pipeline 处理)或使用 Feed exports 存入到文件中。

  因此,CrawlSpider也是以 start_requests() 作为程序入口,之后回调 parse() 函数。但需要注意的是,CrawlSpider已经重载了parse()函数,因此,我们不能再重写parse()函数,否则程序将会出错。

def parse(self, response):
    return self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True)

def parse_start_url(self, response):
    return []

def process_results(self, response, results):
    return results

  我们可以看到,在 CrawlSpider 重载的 parse() 函数中,它的返回是 _parse_response() 函数,而这个函数就是 CrwalSpider 的重点!其源码如下:

def _parse_response(self, response, callback, cb_kwargs, follow=True):
    if callback:
        cb_res = callback(response, **cb_kwargs) or ()
        cb_res = self.process_results(response, cb_res)
        for requests_or_item in iterate_spider_output(cb_res):
            yield requests_or_item

    if follow and self._follow_links:
        for request_or_item in self._requests_to_follow(response):
            yield request_or_item

  对比两段源码,我们不难发现,_parse_response() 函数中的回调函数 callback 就是 parse_start_url() 方法,我们可以 重载 parse_start_url() 方法,进行逻辑处理
  此外,_parse_response() 函数还调用 process_results() 方法对 parse_start_url() 返回的数组做进一步的处理,因此,我们也可以 重载process_results() 做进一步的逻辑处理。
  接着就是整个CrawlSpider的重中之重了!
  在_parse_response() 传递的参数中,我们可以在末尾看到 follow=True ,这表示 CrawlSpider 会对请求做进一步跟踪。其调用的 _requests_to_follow () 源码如下:

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)

  方法进行以下操作:
1. 检测传入的参数是不是 response,如果不是则直接返回。
2. 根据传入的参数 self._rules 做进一步的处理
3. 对links进行去重处理
4. 调用 self._build_request(n, link) 对link 进行下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值