Pyspider框架(二)

pyspider框架的架構(gòu)

1.概述

下圖顯示了pyspider體系結(jié)構(gòu)及其組件的概述,以及系統(tǒng)內(nèi)部發(fā)生的數(shù)據(jù)流的概要安疗。

組件之間通過(guò)消息隊(duì)列進(jìn)行連接人芽。每一個(gè)組件都包含消息隊(duì)列棉姐,都在它們自己的進(jìn)程/線程中運(yùn)行,并且是可以替換的鸵赖。這意味者毕贼,當(dāng)處理速度緩慢時(shí)温赔,這個(gè)時(shí)候我們可以通過(guò)啟動(dòng)多個(gè)processor實(shí)例來(lái)充分利用多核cpu來(lái)進(jìn)行提高效率,或者進(jìn)行分布式部署來(lái)提高效率鬼癣。

2.組件

(1)Scheduler(調(diào)度器)

調(diào)度器從processor返回的新任務(wù)隊(duì)列中接收任務(wù)陶贼。判斷是新任務(wù)還是需要重新爬取啤贩。通過(guò)優(yōu)先級(jí)對(duì)任務(wù)進(jìn)行分類,并且利用令牌桶算法將任務(wù)發(fā)送給fetcher拜秧, 處理周期任務(wù)痹屹,丟失的任務(wù)和失敗的任務(wù),并且稍后重試枉氮。

以上都可以通過(guò)self.crawl進(jìn)行設(shè)置(我們的第一個(gè)腳本就是一個(gè)processor)志衍。

注意,在當(dāng)前的調(diào)度器實(shí)現(xiàn)中聊替,只允許一個(gè)調(diào)度器去進(jìn)行運(yùn)行楼肪。

(2)Fetcher(本質(zhì)就是一個(gè)請(qǐng)求,只是將Scheduler發(fā)出的請(qǐng)求進(jìn)行處理后變成結(jié)果惹悄,而對(duì)于Processor來(lái)說(shuō)它是一個(gè)請(qǐng)求)---爬取器

Fetcher的職責(zé)是獲取web頁(yè)面然后把結(jié)果發(fā)送給processor春叫。請(qǐng)求method, headers泣港, cookies暂殖, proxy, etag 等当纱,都可以設(shè)置央星。

(3)Processor(如果返回結(jié)果就輸出,如果返回進(jìn)行craw()方法就傳給Sheduler)---處理器

處理器的職責(zé)是運(yùn)行用戶編寫(xiě)的腳本惫东,去解析和提取信息。您的腳本在無(wú)限制的環(huán)境中運(yùn)行毙石。盡管我們有各種各樣的工具(如PyQuery廉沮,還可以用xpath和beautifulsoup)供您提取信息和連接,您可以使用任何您想使用的方法來(lái)處理響應(yīng)徐矩。

處理器會(huì)捕捉異常和記錄日志滞时,發(fā)送狀態(tài)(任務(wù)跟蹤)和新的任務(wù)給調(diào)度器,發(fā)送結(jié)果給Result Worker滤灯。

(4)Result Worker

Result WorkerPorcess接收結(jié)果數(shù)據(jù)坪稽。Pyspider有一個(gè)內(nèi)置的結(jié)果處理器將數(shù)據(jù)保存到resultdb,根據(jù)您的需要重寫(xiě)它以處理結(jié)果。

(5)WebUI

WebUI是一個(gè)面向內(nèi)容的web前端鳞骤。它包含:

  • 腳本編輯器窒百,調(diào)試器
  • 項(xiàng)目管理器
  • 任務(wù)監(jiān)控程序
  • 結(jié)果查看器和導(dǎo)出

也許webui是pyspider最吸引人的地方。使用這個(gè)強(qiáng)大的UI豫尽,您可以像pyspider一樣一步一步地調(diào)試腳本篙梢。啟動(dòng)停止項(xiàng)目。找到哪個(gè)項(xiàng)目出錯(cuò)了美旧,什么請(qǐng)求失敗了渤滞,然后使用調(diào)試器再試一次贬墩。

(6)Data flow

pyspider中的數(shù)據(jù)流如上圖所示:

  1. 當(dāng)您按下WebUI上的Run按鈕時(shí),每個(gè)腳本都有一個(gè)名為on_start的回調(diào)妄呕。作為項(xiàng)目的入口陶舞,on_start產(chǎn)生的新任務(wù)將會(huì)提交給調(diào)度器。
  2. 調(diào)度程序使用一個(gè)數(shù)據(jù)URI將這個(gè)on_start任務(wù)分派為要獲取的普通任務(wù)绪励。
  3. Fetcher對(duì)它發(fā)出一個(gè)請(qǐng)求和一個(gè)響應(yīng)(對(duì)于數(shù)據(jù)URI肿孵,它是一個(gè)假的請(qǐng)求和響應(yīng),但與其他正常任務(wù)沒(méi)有區(qū)別)优炬,然后送給處理器颁井。
  4. 處理器調(diào)用on_start方法并生成一些要抓取的新URL。處理器向調(diào)度程序發(fā)送一條消息蠢护,說(shuō)明此任務(wù)已完成雅宾,并通過(guò)消息隊(duì)列將新任務(wù)發(fā)送給調(diào)度程序(在大多數(shù)情況下,這里沒(méi)有on_start的結(jié)果葵硕。如果有結(jié)果眉抬,處理器將它們發(fā)送到result_queue)。
  5. 調(diào)度程序接收新任務(wù)懈凹,在數(shù)據(jù)庫(kù)中查找蜀变,確定任務(wù)是新的還是需要重新抓取,如果是介评,將它們放入任務(wù)隊(duì)列库北,按順序分派任務(wù)。
  6. 這個(gè)過(guò)程重復(fù)(從步驟3開(kāi)始)们陆,直到WWW死后才停止;)寒瓦。調(diào)度程序?qū)z查定期任務(wù),以抓取最新數(shù)據(jù)坪仇。

3.關(guān)于任務(wù)

任務(wù)是調(diào)度的基本單元(就是進(jìn)行調(diào)度的過(guò)程就是對(duì)任務(wù)進(jìn)行調(diào)度)杂腰。

(1)基本原理
  • 任務(wù)由它的taskid(默認(rèn):md5(url), 可以通過(guò)重寫(xiě)get_taskid(self, task)方法來(lái)修改,在第一個(gè)腳本里重寫(xiě)函數(shù)方法)來(lái)區(qū)分椅文。
  • 不同項(xiàng)目之間的任務(wù)是隔離的喂很。
  • 一個(gè)任務(wù)有4個(gè)狀態(tài):
    • active
    • failed
    • success
    • bad-not used
  • 只有處于active狀態(tài)的任務(wù)會(huì)被調(diào)度。
  • 任務(wù)按優(yōu)先次序執(zhí)行皆刺。
(2)調(diào)度
a少辣、新任務(wù)

當(dāng)一個(gè)新的任務(wù)(從未見(jiàn)過(guò))出現(xiàn):

  • 如果設(shè)置了exetime但沒(méi)有到達(dá),它將被放入一個(gè)基于時(shí)間的隊(duì)列中等待芹橡。
  • 否則將被接受毒坛。

當(dāng)任務(wù)已經(jīng)在隊(duì)列中:

  • 除非(force_update)強(qiáng)制更新,否則忽略

當(dāng)一個(gè)完成過(guò)的任務(wù)出現(xiàn)時(shí)(重復(fù)的任務(wù)):

  • 如果age設(shè)置,且last_crawl_time + age < now(上一次的抓取時(shí)間+age設(shè)置的時(shí)間小于當(dāng)前時(shí)間) 它將會(huì)被接受煎殷,否則拋棄屯伞。
  • 如果itag設(shè)置,且它不等于上一次的值豪直,它會(huì)被接受劣摇,否則拋棄(進(jìn)行itag設(shè)置使得所有的新任務(wù)進(jìn)行啟動(dòng)一遍)。
b弓乙、 重試

當(dāng)請(qǐng)求錯(cuò)誤或腳本錯(cuò)誤發(fā)生時(shí)末融,任務(wù)將在默認(rèn)情況下重試3次。

第一次重試將在30秒暇韧、1小時(shí)勾习、6小時(shí)、12小時(shí)后每次執(zhí)行懈玻,任何更多的重試將推遲24小時(shí)巧婶。retry_delay是一個(gè)指定重試間隔的字典。這個(gè)字典中的元素是{retried:seconds}, 如果未指定涂乌,則使用特殊鍵:''空字符串指定的默認(rèn)推遲時(shí)間艺栈。

例如默認(rèn)的retry_delay聲明如下:

class MyHandler(BaseHandler):
    retry_delay = {
        0: 30,
        1: 1*60*60,
        2: 6*60*60,
        3: 12*60*60,
        '': 24*60*60
    }

4.關(guān)于項(xiàng)目

在大多數(shù)情況下,項(xiàng)目是為一個(gè)網(wǎng)站編寫(xiě)的一個(gè)腳本(一個(gè)網(wǎng)站一個(gè)項(xiàng)目)湾盒。

  • 項(xiàng)目是獨(dú)立的湿右,但是可以用from Projects import other_project將另一個(gè)項(xiàng)目作為模塊導(dǎo)入。
  • 一個(gè)項(xiàng)目有5個(gè)狀態(tài):TODO,STOP,CHECKING,DEBUGRUNNING
    • TODO - 剛創(chuàng)建罚勾,正在編寫(xiě)腳本
    • 如果您希望項(xiàng)目停止(= =)毅人,可以將其標(biāo)記為STOP。
    • CHECKING - 當(dāng)正在運(yùn)行的項(xiàng)目被修改時(shí)尖殃,為了防止未完成的修改堰塌,項(xiàng)目狀態(tài)將被設(shè)置為自動(dòng)檢查。
    • DEBUG/RUNNING - 這兩種狀態(tài)對(duì)爬蟲(chóng)沒(méi)有區(qū)別分衫。但最好在第一次運(yùn)行時(shí)將其標(biāo)記為DEBUG,然后在檢查后將其更改為RUNNING般此。
  • 爬行速率由rateburst并采用令牌桶算法進(jìn)行控制蚪战。
    • rate - 一秒鐘內(nèi)有多少請(qǐng)求(即一秒鐘爬多少次)
    • burst (爆發(fā))- 考慮這種情況,RATE/BURST=0.1/3铐懊,這意味著蜘蛛每10秒抓取1個(gè)頁(yè)面邀桑。所有任務(wù)都已完成,Project將每分鐘檢查最后更新的項(xiàng)目科乎。假設(shè)找到3個(gè)新項(xiàng)目壁畸,pyspider將“爆發(fā)”并爬行3個(gè)任務(wù),而不等待3*10秒。但是捏萍,第四個(gè)任務(wù)需要等待10秒太抓。
  • 若要?jiǎng)h除項(xiàng)目,請(qǐng)將“組”(group)設(shè)置為“刪除”(delete)令杈,將“狀態(tài)”設(shè)置為“停止”走敌,然后等待24小時(shí)。
回調(diào) on_finished

您可以在項(xiàng)目中重寫(xiě)on_finished方法逗噩,當(dāng)task_queue變?yōu)?時(shí)將觸發(fā)該方法掉丽。

第一種情況:當(dāng)您啟動(dòng)一個(gè)項(xiàng)目來(lái)抓取一個(gè)包含100個(gè)頁(yè)面的網(wǎng)站時(shí),當(dāng)100個(gè)頁(yè)面被成功抓取或重試失敗時(shí)异雁,on_finished回調(diào)將被觸發(fā)捶障。

第二種情況:帶有auto_recrawl任務(wù)的項(xiàng)目永遠(yuǎn)不會(huì)觸發(fā)on_finished回調(diào),因?yàn)楫?dāng)其中有auto_recrawl任務(wù)時(shí)纲刀,時(shí)間隊(duì)列永遠(yuǎn)不會(huì)變?yōu)?项炼。

第三種情況:帶有@every修飾方法的項(xiàng)目將在每次新提交的任務(wù)完成時(shí)觸發(fā)on_finished回調(diào)。

5. 腳本環(huán)境

(1) 變量
  • self.project_name

  • self.project (當(dāng)前項(xiàng)目的詳細(xì)信息)

  • self.response

  • self.task

官方文檔:http://docs.pyspider.org/en/latest/

(2)關(guān)于腳本
  • handler的名稱并不重要柑蛇,但您需要至少一個(gè)繼承basehandler的類芥挣。

  • 可以設(shè)置第三個(gè)參數(shù)來(lái)獲取任務(wù)對(duì)象:def callback(self,response,task)

  • 默認(rèn)情況下耻台,非200響應(yīng)不會(huì)提交回調(diào)空免。可以使用@catch_status_code_error來(lái)處理非200響應(yīng)盆耽。

(3)關(guān)于環(huán)境
  • logging,print以及異常會(huì)被捕獲蹋砚。

  • 你可以通過(guò)from projects import some_project將其他項(xiàng)目當(dāng)做模塊導(dǎo)入。

(4)Web view
  • 以瀏覽器呈現(xiàn)的方式查看頁(yè)面(大約)
(5)HTML view
  • 查看當(dāng)前回調(diào)的HTML(索引頁(yè)摄杂、細(xì)節(jié)頁(yè)等)
(6) Follows view
  • 查看可從當(dāng)前回調(diào)進(jìn)行的回調(diào)(數(shù)量坝咐,即回調(diào)任務(wù)的數(shù)量和相應(yīng)的url)

  • 索引頁(yè)面跟隨視圖將顯示可執(zhí)行的詳細(xì)頁(yè)面回調(diào)。

(7)Messages view
  • 顯示self.send_message發(fā)送的消息
(8)Enable CSS Selector Helper
  • 啟用Web視圖的CSS選擇器幫助程序析恢。它獲取您單擊的元素的CSS選擇器墨坚,然后將其添加到腳本中。

6.處理結(jié)果

從WebUI下載和查看數(shù)據(jù)很方便映挂,但可能不適用于計(jì)算機(jī)泽篮,所以要存儲(chǔ)到自己的數(shù)據(jù)庫(kù)中。

(1)使用resultdb

雖然resultdb僅用于結(jié)果預(yù)覽柑船,但不適用于大規(guī)模存儲(chǔ)結(jié)果數(shù)據(jù)帽撑。

但是,如果您想從resultdb中獲取數(shù)據(jù)鞍时,那么有一些使用數(shù)據(jù)庫(kù)API的簡(jiǎn)單案例可以幫助您連接和選擇數(shù)據(jù)亏拉。

from pyspider.database import connect_database
resultdb = connect_database("<your resutldb connection url>")
for project in resultdb.projects:
    for result in resultdb.select(project):
        assert result['taskid']
        assert result['url']
        assert result['result']

result['result']是由你編寫(xiě)的腳本中的RETURN語(yǔ)句提交的對(duì)象扣蜻。

(2)使用ResultWorker

在生產(chǎn)環(huán)境中,你可能希望將pyspider連接到你的系統(tǒng)的處理管道及塘,而不是將結(jié)果存儲(chǔ)到resultdb中莽使。強(qiáng)烈建議重寫(xiě)ResultWorker。

from pyspider.result import ResultWorker

class MyResultWorker(ResultWorker):
    def on_result(self, task, result):
        assert task['taskid']
        assert task['project']
        assert task['url']
        assert result
        # your processing code goes here

result是你腳本中return語(yǔ)句提交的對(duì)象磷蛹。

您可以將這個(gè)腳本(例如吮旅,my_result_worker.py)放在啟動(dòng)pyspider的文件夾中。

result_worker子命令添加參數(shù):

pyspider result_worker --result-cls=my_result_worker.MyResultWorker (運(yùn)行命令的時(shí)候就這樣運(yùn)行)

或者,如果你使用配置文件

{
  ...
  "result_worker": {
    "result_cls": "my_result_worker.MyResultWorker"
  }
  ...
}

為了兼容性味咳,將存儲(chǔ)在數(shù)據(jù)庫(kù)中的結(jié)果編碼為JSON(因?yàn)閞esult_worker存儲(chǔ)到數(shù)據(jù)庫(kù)的數(shù)據(jù)都是JSON類型的)庇勃。強(qiáng)烈建議您設(shè)計(jì)自己的數(shù)據(jù)庫(kù),并覆蓋上面描述的ResultWorker槽驶。

(3)關(guān)于結(jié)果的小技巧
想要在回調(diào)中返回多個(gè)結(jié)果?

resultdb會(huì)通過(guò)taskid(url)對(duì)結(jié)果進(jìn)行去重责嚷,后面的結(jié)果會(huì)覆蓋前面的(但是在腳本中每個(gè)任務(wù)只會(huì)返回一個(gè)結(jié)果,如果有多個(gè)結(jié)果的話只會(huì)返回最后一個(gè)數(shù)據(jù)結(jié)果)掂铐。

一個(gè)解決方案是使用send_message API為每個(gè)結(jié)果生成一個(gè)偽taskid罕拂。

def detail_page(self, response):
    for li in response.doc('li').items():
        self.send_message(self.project_name, {
            ...寫(xiě)入要發(fā)的結(jié)果
         #   "url": response.url,
           #  "title": response.doc('title').text(),
        
        }, url=response.url+"#"+li('a.product-sku').text())

def on_message(self, project, msg):#這個(gè)方法是返回結(jié)果,當(dāng)上一個(gè)方法每進(jìn)行一次send_message就會(huì)調(diào)用這個(gè)方法全陨,就會(huì)返回相應(yīng)的結(jié)果爆班。
    return msg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市辱姨,隨后出現(xiàn)的幾起案子柿菩,更是在濱河造成了極大的恐慌,老刑警劉巖雨涛,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枢舶,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡替久,警方通過(guò)查閱死者的電腦和手機(jī)凉泄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蚯根,“玉大人后众,你說(shuō)我怎么就攤上這事÷梗” “怎么了吼具?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)矩距。 經(jīng)常有香客問(wèn)我,道長(zhǎng)怖竭,這世上最難降的妖魔是什么锥债? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上哮肚,老公的妹妹穿的比我還像新娘登夫。我一直安慰自己,他們只是感情好允趟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布恼策。 她就那樣靜靜地躺著,像睡著了一般潮剪。 火紅的嫁衣襯著肌膚如雪涣楷。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天抗碰,我揣著相機(jī)與錄音狮斗,去河邊找鬼。 笑死弧蝇,一個(gè)胖子當(dāng)著我的面吹牛碳褒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播看疗,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼沙峻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了两芳?” 一聲冷哼從身側(cè)響起摔寨,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎盗扇,沒(méi)想到半個(gè)月后祷肯,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡疗隶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年佑笋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斑鼻。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蒋纬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出坚弱,到底是詐尸還是另有隱情蜀备,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布荒叶,位于F島的核電站碾阁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏些楣。R本人自食惡果不足惜脂凶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一宪睹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蚕钦,春花似錦亭病、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至邮屁,卻和暖如春整袁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背樱报。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工葬项, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人迹蛤。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓民珍,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親盗飒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嚷量,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容