1. 程式人生 > >scrapy -->CrawlSpider 介紹

scrapy -->CrawlSpider 介紹

scrapy -->CrawlSpider 介紹


 

1、首先,通過crawl 模板新建爬蟲:

scrapy genspider -t crawl lagou www.lagou.com

 

創建出來的爬蟲檔案lagou.py:

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule


class LagouSpider(CrawlSpider):
    name 
= 'lagou' allowed_domains = ['www.lagou.com'] start_urls = ['http://www.lagou.com/'] rules = ( Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True), ) def parse_item(self, response): i = {} #i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract()
#i['name'] = response.xpath('//div[@id="name"]').extract() #i['description'] = response.xpath('//div[@id="description"]').extract() return i
lagou.py

 

2、CrawlSpider 概述及原始碼分析

1)CrawlSpider概述

 CrawlSpider使用於取全站的話。CrawlSpider基於Spider,但是可以說是為全站爬取而生。CrawlSpiders是Spider的派生類,Spider類的設計原則是隻爬取start_url列表中的網頁,而CrawlSpider類定義了一些規則(rule)來提供跟進link的方便的機制,從爬取的網頁中獲取link並繼續爬取的工作更適合。

2)CrawlSpider的原始碼分析 → 方法介紹

①、當我們啟動爬蟲時,爬取url是從Spider(CrawlSpider繼承於Spider)下的 start_requests 函式開始執行的,該方法中會逐個獲取lagou.py中 start_urls列表中的url進行request爬取資料並yield出去

# Spider類

    def start_requests(self):
        cls = self.__class__
        if method_is_overridden(cls, Spider, 'make_requests_from_url'):
            warnings.warn(
                "Spider.make_requests_from_url method is deprecated; it "
                "won't be called in future Scrapy releases. Please "
                "override Spider.start_requests method instead (see %s.%s)." % (
                    cls.__module__, cls.__name__
                ),
            )
            for url in self.start_urls:
                yield self.make_requests_from_url(url)
        else:
            for url in self.start_urls:
                yield Request(url, dont_filter=True)

 

②、上述每個url爬取完成時會自動呼叫回撥函式:parse函式 

parse函式作用:本來由parse函式處理start_urls中的url返回的資料,在CrawlSpider中的parse函式中,是將start_urls中的每個url的返回值response交由_parse_response函式進行處理, 同時設定 parse_start_url 為回撥函式(parse_start_url在原始碼中只返回了空列表,也就是說如果我們要對start_urls中每個url的返回值做處理,過載parse_start_url函式並進行自己程式碼的編寫就可以了。如上述中start_urls中只有一個url:www.lagou.com )↓

 

③、_parse_response函式作用:

1)如果回撥函式是parse_start_url,執行該函式,yield request ; 如果回撥函式是rules.callback,執行該函式並yield item轉pipelines進行資料處理

2)到rules(規則)中查詢follow是否為 True,True表示需要跟進,接著呼叫_requests_to_follow函式↓

需要注意的是:parse函式是CrawlSpider原始碼中已經定義好的函式,我們儘量不能過載這函式,避免出錯。可以過載的是parse_start_url、process_results 這兩個函式

 

④、_requests_to_follow函式作用:

1)根據使用者自定義的rules規則,提取rules中的link連結,將其新增進set集合(避免重複)

2)呼叫_build_request函式,封裝下一層url並進行資料爬取(首頁url →下一層url) ,每個url資料爬取完畢後都呼叫回撥函式:_response_downloaded↓

3)呼叫process_request,即_compile_rules函式下的:rule.process_request = get_method(rule.process_request) ,目的是將rule中的字串表示的方法對映成真正的方法

 

⑤、_response_downloaded函式實際呼叫的是第③步的_parse_response函式:第二層(首層url是首頁)每個url爬取完成後回撥函式真正執行的地方,如果有第三層url(符號規則),則會繼續深度式往第三層、第四層進行資料爬取。

 

#呼叫順序:
  # start_request → 1、 parse → 2、 _parse_response → 3、 parse_start_url → 4、process_results → 5&6、 同時,_parse_response呼叫_follow_lineks
  # → 7、_build_request → 8、_response_downloaded → 返回執行2、_parse_response ,如此迴圈
# 目的:通過start_request對拉勾網首頁頁面進行爬取,提取首頁頁面的所有url,並進行規則匹配。匹配成功則進入該url頁面提取我們需要的目標資料
class CrawlSpider(Spider): rules = () def __init__(self, *a, **kw): super(CrawlSpider, self).__init__(*a, **kw) self._compile_rules() # 1、Spider中的start_request呼叫parse回撥函式,parse函式直接呼叫parse_response函式 def parse(self, response): return self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True) # 3、parse的回撥函式 def parse_start_url(self, response): return [] # 4、直接返回response物件 def process_results(self, response, results): return results # 7、封裝url並進行資料爬取,爬取完成後呼叫回撥函式:_response_downloaded 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 # 6、根據使用者自定義的rules規則,提取rules規則中的link連結,將其新增進set集合(避免重複) def _requests_to_follow(self, response): if not isinstance(response, HtmlResponse): return seen = set() # 抽取rules中LinkExtractor -->allow的規則內容,n為index值,rule為allow中的正則匹配內容,如上面lagou.py中:LinkExtractor(allow=r'Items/')裡面的r'Items/' for n, rule in enumerate(self._rules): # 將rule連結(此時rule連結:http://www.lagou.com/Items/),存入set集合(去重),用於匹配我們爬取url,是否符合該連結要求 links = [lnk for lnk in rule.link_extractor.extract_links(response) if lnk not in seen] if links and rule.process_links: # 使用使用者自定義的process_links處理rules連結,預設不處理 links = rule.process_links(links) for link in links: seen.add(link) # 新增到set集合,去重 # 呼叫_build_request函式,將 r = self._build_request(n, link) # 對每個Request呼叫process_request()函式。該函式預設為indentify即不做處理,但本函式是呼叫了_compile_rules函式,作用看該函式。 yield rule.process_request(r) # 8、執行_build_request 資料爬取完成後的回撥函式,實際上是呼叫第2步中的_parse_response函式,來執行回撥函式,然後又返到第3步進行處理,如此迴圈。 def _response_downloaded(self, response): # 找到當前對應的rules規則 rule = self._rules[response.meta['rule']] # rule.callback:當前rules規則的callback函式,讓_parse_response執行 return self._parse_response(response, rule.callback, rule.cb_kwargs, rule.follow) # 2、parse、rule下的回撥函式的實際執行函式,及是否跟進的判斷 def _parse_response(self, response, callback, cb_kwargs, follow=True): if callback: # 回撥函式有兩種: # 1)parse函式的回撥函式(parse_start_url()),用parse_start_url()處理response物件,返回Request物件 # 2)如果是rules規則中呼叫的callback函式,則返回的是Item物件 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 # yield request:執行的是parse的回撥函式。 or yield item:表示該url資料爬取完畢,轉到pipelines進行資料處理 # 5、每個url爬取後,可能會爬取出一些url,通過下面判斷是否跟進對這些url進行下一層資料爬取 if follow and self._follow_links: # 需要跟進下一層url的爬取,呼叫_follow_lineks函式進行處理 for request_or_item in self._requests_to_follow(response): yield request_or_item def _compile_rules(self): # 將rule中的字串表示的方法對映成真正的方法 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)

 圖解析: