趙 宸 劉建華
(西安郵電大學(xué) 西安 710121)
網(wǎng)絡(luò)爬蟲[9]是一種按照一定的規(guī)則,自動(dòng)地抓取萬(wàn)維網(wǎng)信息的程序或者腳本,目前主流的爬蟲框架Nutch、Crawler4j、WebMagic、scrapy、WebCollector均沒(méi)有在運(yùn)行的爬蟲中實(shí)時(shí)更新代碼并自動(dòng)重新載入的功能,本文利用Django 框架[2]中自動(dòng)重載機(jī)制,使用Python[6]語(yǔ)言開發(fā)了分布式[4~5]、多進(jìn)程爬蟲框架[1],實(shí)現(xiàn)了可在運(yùn)行的爬蟲中實(shí)時(shí)更新代碼并自動(dòng)重新載入內(nèi)存執(zhí)行的功能,同時(shí),根據(jù)下載過(guò)程中的斷點(diǎn)續(xù)傳[7]思想,使用Redis[3,16]開發(fā)了任務(wù)隊(duì)列技術(shù),實(shí)現(xiàn)了重新載入爬蟲代碼后從斷點(diǎn)恢復(fù)任務(wù)狀態(tài)并繼續(xù)執(zhí)行后續(xù)任務(wù)的功能,并解決了重新載入后數(shù)據(jù)丟失問(wèn)題。實(shí)驗(yàn)表明,爬蟲框架可識(shí)別爬蟲代碼變化并重新載入到內(nèi)存運(yùn)行,重新載入后不會(huì)丟失數(shù)據(jù),且相對(duì)其他分布式網(wǎng)絡(luò)爬蟲,性能提升了40.7%,節(jié)約了數(shù)據(jù)獲取成本。
爬蟲框架基于Django框架開發(fā),由集中式的爬蟲服務(wù)和任務(wù)隊(duì)列以及分布式的爬蟲實(shí)例構(gòu)成,并利用了Django 框架的ORM[10]封裝了數(shù)據(jù)處理模塊。如圖1 所示,爬蟲服務(wù)將用戶初始任務(wù)存入任務(wù)隊(duì)列中,由分布式爬蟲實(shí)例中的爬蟲主進(jìn)程獲取并分配給爬蟲子進(jìn)程執(zhí)行與處理,所有爬蟲實(shí)例的結(jié)構(gòu)與內(nèi)容相同,保證了任務(wù)處理的負(fù)載均衡。
爬蟲實(shí)例由爬蟲主進(jìn)程、爬蟲子進(jìn)程、任務(wù)模塊、數(shù)據(jù)處理模塊、抓取器、下載器、解析器、日志模塊和各站點(diǎn)爬蟲組成等組成,所有模塊均可調(diào)用日志模塊并輸出日志。爬蟲實(shí)例執(zhí)行任務(wù)流程使用偽代碼描述如下:
圖1 爬蟲框架結(jié)構(gòu)圖
首先,Django框架的自動(dòng)重載機(jī)制[11]實(shí)現(xiàn)原理是每隔1 秒根據(jù)每個(gè)文件的最后修改時(shí)間來(lái)判斷文件是否被修改,如果修改,則退出舊進(jìn)程,啟動(dòng)新進(jìn)程執(zhí)行修改后的代碼。爬蟲實(shí)例的自動(dòng)重載基于這一原理實(shí)現(xiàn),首先,將爬蟲框架的基本組件和爬蟲文件組織成Django 項(xiàng)目中的APP 包,如圖2 所示。然后,改寫Django 的命令行模塊,使原來(lái)的啟動(dòng)站點(diǎn)服務(wù)的命令行功能變成執(zhí)行爬蟲主進(jìn)程,并設(shè)置運(yùn)行參數(shù)為“—reload”,從而將爬蟲框架和爬蟲代碼列入自動(dòng)重載機(jī)制中,這樣更新爬蟲代碼時(shí),Django 框架會(huì)通過(guò)重載重新執(zhí)行爬蟲主進(jìn)程,從而執(zhí)行新的爬蟲代碼。
圖2 爬蟲實(shí)例結(jié)構(gòu)
在分布式環(huán)境下,爬蟲主進(jìn)程重新載入后,為了找回?cái)帱c(diǎn)以及防止正在執(zhí)行的任務(wù)丟失,本文利用Redis設(shè)計(jì)了爬蟲框架的任務(wù)隊(duì)列系統(tǒng)。
如圖3 所示,除了“任務(wù)隊(duì)列”,還設(shè)計(jì)了“本地正在執(zhí)行的任務(wù)隊(duì)列”。爬蟲主進(jìn)程將調(diào)度成功的任務(wù)同步寫入“本地正在執(zhí)行的任務(wù)隊(duì)列”,發(fā)生爬蟲主進(jìn)程重載時(shí),會(huì)重新執(zhí)行“本地正在執(zhí)行的任務(wù)隊(duì)列”中的任務(wù),當(dāng)爬蟲子進(jìn)程完成任務(wù)后會(huì)從該隊(duì)列中刪除任務(wù)。同時(shí),為了保證重新載入過(guò)程中不對(duì)數(shù)據(jù)讀寫產(chǎn)生影響,框架中對(duì)Redis 和數(shù)據(jù)庫(kù)的操作兼具備原子性[12],使用管道技術(shù)讀寫Re?dis 以及封裝數(shù)據(jù)庫(kù)讀寫都保證了重新載入時(shí)的操作回滾。
圖3 Redis任務(wù)隊(duì)列
初始任務(wù)在寫入任務(wù)隊(duì)列時(shí),由框架分配唯一ID,爬蟲由此任務(wù)不斷產(chǎn)生的新任務(wù)共用這個(gè)ID,在分布式環(huán)境中,爬蟲主進(jìn)程判斷一個(gè)任務(wù)完成的標(biāo)志由式(1)計(jì)算得出,其中,NR為此ID 任務(wù)剩余數(shù)量,NA為新任務(wù)數(shù)量,每個(gè)分布式爬蟲實(shí)例新增一個(gè)任務(wù),即增1,NF為已完成的任務(wù)數(shù)量,在爬蟲子進(jìn)程釋放資源時(shí)設(shè)置,NE為出錯(cuò)的任務(wù)數(shù)量,判斷此ID 任務(wù)完成的條件為:NR=0。例如,完成初始任務(wù)后,產(chǎn)生一個(gè)新任務(wù),則在釋放資源前對(duì)NA進(jìn)行加一操作,此時(shí)NR為1>0。在完成非初始任務(wù)的這個(gè)任務(wù)后,如果又產(chǎn)生2 個(gè)新任務(wù),則在釋放資源前先經(jīng)行NF增1 操作,并對(duì)NR進(jìn)行增2 操作,此時(shí)NR為2>0。當(dāng)完成新產(chǎn)生的2 個(gè)任務(wù)且未產(chǎn)生新任務(wù)時(shí),做NF增2 操作,此時(shí)NR為0,可判斷任務(wù)結(jié)束。
測(cè)試目的:測(cè)試分布式爬蟲實(shí)例在運(yùn)行過(guò)程中更新代碼,是否可以自動(dòng)重新載入且不丟失數(shù)據(jù)。
測(cè)試環(huán)境:在內(nèi)網(wǎng)IP 末尾分別為239 和240 兩臺(tái)Linux 服務(wù)器[13]上部署爬蟲實(shí)例,在239 服務(wù)器上部署爬蟲服務(wù)和任務(wù)隊(duì)列。
測(cè)試方法:發(fā)送4個(gè)相同的爬蟲任務(wù),在240節(jié)點(diǎn)爬蟲實(shí)例運(yùn)行過(guò)程中,多次使用git[14~15]將添加了空格的爬蟲代碼從代碼倉(cāng)庫(kù)直接同步到本地,測(cè)試爬蟲實(shí)例自動(dòng)重新載入的情況。
測(cè)試結(jié)果:如圖4 所示,代碼發(fā)生變化后,運(yùn)行中的爬蟲實(shí)例實(shí)現(xiàn)了自動(dòng)重新載入。由圖5 可知,在分布式環(huán)境中,4 個(gè)相同的任務(wù)在有爬蟲節(jié)點(diǎn)重載的情況下,最終獲取的數(shù)據(jù)數(shù)量相同。
圖4 爬蟲重載測(cè)試
圖5 爬蟲重載測(cè)試結(jié)果
結(jié)論:運(yùn)行中的爬蟲實(shí)例可在代碼更新后實(shí)現(xiàn)自動(dòng)重新載入,并且任意時(shí)刻的重新載入不會(huì)對(duì)數(shù)據(jù)結(jié)果產(chǎn)生影響。
測(cè)試目的:測(cè)試爬蟲獲取數(shù)據(jù)的速度,并與已有分布式爬蟲對(duì)比。
測(cè)試環(huán)境:為保證對(duì)比測(cè)試環(huán)境統(tǒng)一,調(diào)整測(cè)試環(huán)境:Cpu核心數(shù)為12個(gè),網(wǎng)絡(luò)1Gbps,內(nèi)存8G。
測(cè)試方法:運(yùn)行鏈家二手房爬蟲,發(fā)送“上海市”關(guān)鍵詞為初始任務(wù),爬取上海市鏈家站點(diǎn)的全部二手房掛牌房源信息,每條房源數(shù)據(jù)需分別請(qǐng)求詳細(xì)頁(yè)和過(guò)去看房次數(shù)頁(yè)面。
測(cè)試結(jié)果:圖6 中方框分別對(duì)應(yīng)第一條數(shù)據(jù)和最后一條數(shù)據(jù)的入庫(kù)時(shí)間,由此可見,框架中的爬蟲在約77min左右的時(shí)間內(nèi)獲取了54256條房源信息,平均1 秒11.725 條數(shù)據(jù),即下載速度約為23.451(URL/s),平均單進(jìn)程單頁(yè)面耗時(shí)1.023s。
結(jié)論:相比其他分布式網(wǎng)絡(luò)爬蟲[8]同等測(cè)試環(huán)境下的16.666(URL/s)下載速度,該框架下的爬蟲下載速度提升了(23.451-16.666)/16.666=40.7%的性能。
圖6 上海鏈家二手房數(shù)據(jù)
該爬蟲框架是一個(gè)多進(jìn)程、分布式的爬蟲框架,借助Django 框架特性,具有隨時(shí)更新爬蟲代碼后自動(dòng)重新載入的功能,并且重新載入過(guò)程中不會(huì)丟數(shù)據(jù),可使用腳本批量更新集群中的爬蟲代碼,使得爬蟲的開發(fā)和維護(hù)變的十分方便,同時(shí)相較其他分布式爬蟲方案,本文框架可提升爬蟲40.7%的性能,降低了數(shù)據(jù)獲取成本。