看了上一節分析的scrapy
架構,咱們最想了解的,應該就是5大核心模塊是怎麼實現的吧。好,從github中找到各大核心模塊的源碼:html
這些模塊,不是用class實現的,就是用package實現的。看模塊代碼頂多能瞭解它們的功能(成員函數),根本看不出scrapy是怎麼運行的。那麼問題來了,scrapy是怎麼被啓動並運行的?python
從(1)中咱們能夠看到,咱們本身寫一個scrapy程序,這個程序的運行,是從命令scrapy crawl hello
開始的。
那這條命令到底作了哪些事呢?react
crawl
命令的運行scrapy程序的源代碼結構是很是清晰的,能夠「猜」到crawl
命令的代碼在這裏:git
https://github.com/scrapy/scrapy/blob/master/scrapy/commands/crawl.py
看樣子是用command模式寫的,命令會執行run()
中的代碼,去掉異常處理部分,其實crawl
命令最核心的代碼就是下面兩條:github
self.crawler_process.crawl(spname, **opts.spargs) self.crawler_process.start()
只要能找到self.crawler_process.start()
的代碼,就能搞清楚crawl
命令的運行細節了。要搞明白這兩條代碼作了什麼,必須知道crawler_process
是什麼。在github中搜一下這個crawler_process
,發現編程
cmd.crawler_process = CrawlerProcess(settings)
繼續搜CrawlerProcess
(github的搜索功能用起來真是爽)。終於在scrapy/scrapy/crawler.py
裏發現了咱們須要尋找的CrawlerProcess.start()
函數。設計模式
CrawlerProcess.start()
的邏輯經過上面的分析,scrapy程序的啓動,就是執行了self.crawler_process.start()
,跟進去發現實際上是調用了CrawlerProcess.start()
函數。從代碼註釋,可看到這個函數作了那麼幾件事:api
reactor
對象reactor
對象的線程池大小reactor.run(installSignalHandlers=False)
而後這個reactor
就run了,嗯?!這個reactor
是什麼鳥,它run啥內容了呢?緩存
在這個crawler.py
文件中看了一圈,也沒發現往reactor
中傳入什麼handler之類的參數。不過卻是發現shutdown/kill一個reactor是很容易的。網絡
代碼跟到這裏,必需要看一下Twisted
框架的reactor
對象運行機制是怎麼樣的了。
reactor
對象Twisted
是python的異步網絡編程框架。做爲一個框架,它有本身的編程套路。這個套路,就是傳說中的「異步編程模式/事件驅動模式」。 事件驅動的特色是包含一個事件循環(loop),當外部事件發生時用回調機制來觸發相應的事件處理代碼。
而reactor
,就是I/O併發模型中「reactor模式」的實現。從(3)中可知,reactor
就實現了這個事件環(loop)。
而最重要的是,reactor
模式,實現了單線程環境中,調度多個事件源產生的事件到它們各自的事件處理例程中去。藉助這個reactor模式,用單線程,就實現了事件處理機制(callback)。
總結一下,reactor
只是一種設計模式,就是一個代碼框架而已。真正的代碼邏輯,應該在調用reactor.run()
以前就搞定了的。因此想弄明白CrawlerProcess.start()
,得看這個函數調用以前,作了哪些事。
能作啥事呢?也就是CrawlerProcess
類,以及它父類的初始化。
CrawlerProcess
類的父類是CrawlerRunner
類。看看它們的初始化工做都作了啥。
看它的註釋,「keeps track of, manages and runs crawlers inside an already setup Twisted reactor
」。看到了吧,它就是管理reactor
中的各個爬蟲的。
這個類的初始化,主要代碼就是下面兩行:
self.settings = settings self.spider_loader = _get_spider_loader(settings)
加載配置,並根據配置加載spider
。所謂加載spider
,從scrapy/scrapy/spiderloader.py
能夠看出,就是加載各個sipder
的屬性(name等等)。
父類初始化後,子類CrawlerProcess
才執行初始化。而CrawlerProcess
類的功能,根據註釋:run multiple scrapy crawlers in a process simultaneously。就是在單進程中跑多個爬蟲(用twisted的reactor實現)。這個類裏就實現了scrapy的「異步編程模式/事件驅動模式」。
它之因此叫xxxProcess,做者想說的就是「單進程」的意思吧。
這個類除了實現reactor模式,還添加了log
,shutdown
信號處理(Ctrl+C)功能。
理一下,當咱們運行scrapy crawl hello
命令,就啓動了一個scrapy爬蟲,它的啓動過程是這樣的:
spider
(父類CrawlerRunner
類初始化)log
,與shutdown
(Ctrl+C)信號處理機制(子類CrawlerProcess
類初始化)CrawlerProcess.start()
,依次完成下面的邏輯:
reactor
對象,調整這個reactor
對象的線程池大小reactor.run()
。至此,第一步加載的spider
,都放在reactor模式中運行了。每一個spider
有本身的name
,start_urls
等屬性。根據(4),scrapy會爲每個URL建立scrapy.Request
對象,並將spider
的parse()
方法做爲scrapy.Request
對象的callback
函數。