1. 程式人生 > >scrapy原始碼分析(六)---------------CrawlProcess

scrapy原始碼分析(六)---------------CrawlProcess

上一篇教程中講到crawl命令最終會執行CrawlProcess的crawl和start方法。這一篇對CrawlProcess的原始碼進行詳細分析,來了解一下是如何進行爬取任務的。

先看一下CrawlProcess的建構函式:

scrapy/crawler.py:

可以看到這個模組一共有3個類:Crawler,CrawlerRunner,CrawlerProcess.

Crawler代表了一種爬取任務,裡面使用一種spider,CrawlerProcess可以控制多個Crawler同時進行多種爬取任務。

CrawlerRunner是CrawlerProcess的父類,CrawlerProcess通過實現start方法來啟動一個Twisted的reactor,並控制shutdown訊號,比如crtl-C,它還配置頂層的logging模組。

下面結合原始碼對原始碼進行註釋解析:

class CrawlerProcess(CrawlerRunner):def __init__(self, settings=None):
        super(CrawlerProcess, self).__init__(settings) /*使用settings初始化父類CrawlerRunner*/
        install_shutdown_handlers(self._signal_shutdown) /*註冊shutdown訊號(SIGINT, SIGTERM等)的處理*/
        configure_logging(self
.settings) /*配置loggin*/ log_scrapy_info(self.settings) /*記錄scrapy的資訊*/

再分別來看crawl命令最終呼叫的crawlstart函式實現 :

def crawl(self, crawler_or_spidercls, *args, **kwargs):crawler = self.create_crawler(crawler_or_spidercls) /*crawl方法會建立一個Crawler物件,然後呼叫Crawler
的crawl方法開啟一個爬取任務,同時Crawler的crawl方法會返回一個Deferred物件,CrawlerProcess會將這個Deferred物件
加入一個_active集合,然後就可以在必要時結束Crawler,並通過向Deferred中新增_done callback來跟蹤一個Crawler的結束
。*/
return self._crawl(crawler, *args, **kwargs) /*用建立的Crawler物件呼叫_crawl方法*/
def create_crawler(self, crawler_or_spidercls):if isinstance(crawler_or_spidercls, Crawler): /*如果已經是一個Crawler例項則直接返回*/
        return crawler_or_spidercls
    return self._create_crawler(crawler_or_spidercls) /*如果crawler_or_spidercls是一個Spider的子類則建立
一個新的Crawler,如果crawler_or_spidercls是一個字串,則根據名稱來查詢對應的spider並建立一個Crawler例項*/
def _crawl(self, crawler, *args, **kwargs):
self.crawlers.add(crawler)
d = crawler.crawl(*args, **kwargs) /*呼叫Crawler的crawl方法*/
self._active.add(d)
      def _done(result):     /*向deferred新增一個callback,如果Crawler已經結束則從活動集合中移除一個Crawler*/        
          self.crawlers.discard(crawler)        
          self._active.discard(d)        
          return result    
      return d.addBoth(_done)
這裡還需要再分析的就是Crawler物件的crawl方法:
crawl這個函式使用了Twisted的defer.inlineCallbacks裝飾器,表明如果函式中有地方需要阻塞,則不會阻塞整個總流程。
會讓出執行權,關於這個裝飾器的詳細講解請檢視我前面關於Deferred的教程。
@defer.inlineCallbacks
def crawl(self, *args, **kwargs):
    assert not self.crawling, "Crawling already taking place"
self.crawling = True
    try:
        self.spider = self._create_spider(*args, **kwargs) /*建立一個spider,通過呼叫spider的
from_crawler的方法來建立一個spider物件*/
self.engine = self._create_engine() /*建立一個ExecutionEngine執行引擎*/
start_requests = iter(self.spider.start_requests()) /*獲取spider定義的start_requests,這個在教程四中有詳細
講解*/         
yield self.engine.open_spider(self.spider, start_requests) /*呼叫執行引擎開啟spider,關於Execution的原始碼分析將在下
一篇教程中詳解*/        
yield defer.maybeDeferred(self.engine.start) /*啟動執行引擎*/    
    except Exception:
if six.PY2:            
exc_info = sys.exc_info()        
self.crawling = False
if self.engine is not None:            
yield self.engine.close()        
if six.PY2:            
six.reraise(*exc_info)        
raise

現在,還剩CrawlProcess的start函式,原始碼分析如下;
def start(self, stop_after_crawl=True):if stop_after_crawl:
        d = self.join()
        # Don't start the reactor if the deferreds are already fired
if d.called:
            return
d.addBoth(self._stop_reactor)

    reactor.installResolver(self._get_dns_resolver()) /*安裝一個dns快取*/
    tp = reactor.getThreadPool()
    tp.adjustPoolsize(maxthreads=self.settings.getint('REACTOR_THREADPOOL_MAXSIZE')) /*根據配置調整
reactor的執行緒池*/
    reactor.addSystemEventTrigger('before', 'shutdown', self.stop)
    reactor.run(installSignalHandlers=False)  /*啟動reactor*/
這個函式首先呼叫join函式來對前面所有Crawler的crawl方法返回的Deferred物件新增一個_stop_reactor方法,當所有Crawler
物件都結束時用來關閉reactor.