趙鵬 蘇楠 于慧霞
關(guān)鍵詞:Scrapy;高性能;網(wǎng)站狀態(tài);批量采集
一、引言
目前網(wǎng)絡(luò)中的服務(wù)多以7×24 小時(shí)的方式進(jìn)行不間斷工作,由于在網(wǎng)站建設(shè)中主要側(cè)重點(diǎn)放在了功能的實(shí)現(xiàn)上[1],往往忽略了網(wǎng)絡(luò)安全問(wèn)題,導(dǎo)致網(wǎng)站在運(yùn)營(yíng)過(guò)程中對(duì)各類網(wǎng)絡(luò)攻擊行為的防護(hù)較差。在日常監(jiān)測(cè)過(guò)程中,需要實(shí)時(shí)掌握的系統(tǒng)狀態(tài)信息包括但不限于網(wǎng)站是否可用、域名解析是否正常、網(wǎng)頁(yè)內(nèi)容是否被篡改、掛馬等方面。在管理較少的網(wǎng)站時(shí),尚可采用人工的方式進(jìn)行手動(dòng)監(jiān)測(cè),而當(dāng)網(wǎng)站或者網(wǎng)頁(yè)數(shù)量數(shù)以千計(jì)時(shí),如何能在短時(shí)間內(nèi)及時(shí)準(zhǔn)確地批量采集網(wǎng)站狀態(tài)信息是一個(gè)亟須解決的問(wèn)題。
二、相關(guān)技術(shù)介紹
(一)Scrapy
Scrapy 是一個(gè)用Python 編寫的自由且開(kāi)源的網(wǎng)絡(luò)爬蟲框架。其設(shè)計(jì)初衷是用于爬取網(wǎng)絡(luò)[2],但也可用作爬取API 數(shù)據(jù)的網(wǎng)絡(luò)爬蟲。該框架目前由Scrapinghub公司維護(hù)。Scrapy 項(xiàng)目圍繞“蜘蛛”(Spiders)構(gòu)建,Spider 是提供一套指令的自包含的爬網(wǎng)程序。允許開(kāi)發(fā)者重用代碼將便于構(gòu)建和拓展大型的爬蟲項(xiàng)目[3]。
1.Twisted
Twisted 是用Python 實(shí)現(xiàn)的基于事件驅(qū)動(dòng)的網(wǎng)絡(luò)引擎框架,Twisted 支持許多常見(jiàn)的傳輸及應(yīng)用層協(xié)議[5],包括TCP、UDP、SSL/TLS、HTTP、IMAP、SSH、IRC及FTP。就像Python 一樣,Twisted 也具有“內(nèi)置電池”(Batteries-included)的特點(diǎn)。Twisted 對(duì)于其支持的所有協(xié)議都帶有客戶端和服務(wù)器實(shí)現(xiàn),同時(shí)附帶有基于命令行的工具,使得配置和部署產(chǎn)品級(jí)的Twisted 應(yīng)用變得非常方便[6]。
2.Splash
Splash 是一個(gè)JavaScript 渲染服務(wù),同時(shí)也是一個(gè)帶有HTTP API 的輕量級(jí)Web 瀏覽器,使用Twisted 和QT5 在Python3 中實(shí)現(xiàn)。QT Reactor 用于使服務(wù)完全異步,從而允許通過(guò)QT 主循環(huán)利用Webkit 提高并發(fā)性。Splash 的主要功能有:并行處理多個(gè)網(wǎng)頁(yè);獲取HTML結(jié)果和/ 或截圖;關(guān)閉圖像或使用Adblock Plus 規(guī)則來(lái)加快渲染速度;在頁(yè)面上下文中執(zhí)行自定義JavaScript;編寫Lua 瀏覽腳本; 在Splash-Jupyter 筆記本中開(kāi)發(fā)Splash Lua 腳本;獲取 HAR 格式的詳細(xì)渲染信息。
三、系統(tǒng)功能概述
本文設(shè)計(jì)并實(shí)現(xiàn)的高性能批量網(wǎng)站狀態(tài)信息采集系統(tǒng)基于Scrapy 框架實(shí)現(xiàn)。在該框架基礎(chǔ)上自身結(jié)合自身需求設(shè)計(jì)了數(shù)據(jù)預(yù)處理、網(wǎng)站可用性采集、域名解析和網(wǎng)站首頁(yè)信息采集、數(shù)據(jù)持久化存儲(chǔ)共五個(gè)功能模塊。
(一)數(shù)據(jù)預(yù)處理
為了避免因?yàn)榇杉木W(wǎng)站URL 不規(guī)范引起的請(qǐng)求錯(cuò)誤,需要對(duì)待采集網(wǎng)站域名進(jìn)行檢查、清洗及規(guī)整工作。具體功能包括但不限于處理待爬取網(wǎng)站列表中有些URL 沒(méi)有協(xié)議頭、一條記錄包含多個(gè)使用分隔符分隔的多個(gè)URL 等情況。
(二)網(wǎng)站響應(yīng)狀態(tài)碼采集
網(wǎng)站的響應(yīng)狀態(tài)碼可以體現(xiàn)出網(wǎng)站是否正常可用,因此可采集網(wǎng)站響應(yīng)狀態(tài)碼作為網(wǎng)站是否可用的判斷依據(jù)。由于網(wǎng)站的狀態(tài)是復(fù)雜多樣的,甚至是動(dòng)態(tài)變化的。因此若要得到更準(zhǔn)確的狀態(tài)碼,必須處理那些表示網(wǎng)站狀態(tài)異常的響應(yīng)狀態(tài)碼,比如301、302、400、404、501 等。同時(shí)還需要處理由于網(wǎng)絡(luò)原因引起的請(qǐng)求超時(shí)等異常情況。
(三)域名解析信息采集
域名解析信息包括域名、對(duì)應(yīng)IP 地址、歸屬地等。這些信息是通過(guò)查詢特定的API 獲取,這也正是Scrapy框架的主要功能之一。
(四)網(wǎng)站首頁(yè)信息采集
網(wǎng)站首頁(yè)信息具體包括網(wǎng)站標(biāo)題、網(wǎng)站首頁(yè)文本和全屏截圖三類信息。由于目前大量網(wǎng)站都采取了動(dòng)態(tài)網(wǎng)頁(yè)的設(shè)計(jì)模式,使用Scrapy 直接采集無(wú)法獲取最終的網(wǎng)站頁(yè)面狀態(tài)。因此本模塊使用Scrapy-splash 插件與Splash 服務(wù)器相配合,由Scrapy-splash 負(fù)責(zé)發(fā)起請(qǐng)求,Splash 負(fù)責(zé)渲染網(wǎng)頁(yè)并將渲染后的完整網(wǎng)頁(yè)返回給Scrapy。
(五)數(shù)據(jù)持久化存儲(chǔ)
由于Scrapy 最終解析出的數(shù)據(jù)以字典形式呈現(xiàn),而MongoDB 的操作接口均是以字典作為參數(shù),再借助Scrapy 自身提供的管道中間件無(wú)需編寫SQL 語(yǔ)句就可以完成數(shù)據(jù)的存儲(chǔ)工作,更加簡(jiǎn)潔高效。
四、系統(tǒng)功能架構(gòu)設(shè)計(jì)
系統(tǒng)功能架構(gòu)如圖1 所示。
(一)系統(tǒng)功能實(shí)現(xiàn)
1. 數(shù)據(jù)預(yù)處理
BaseSpider 模塊負(fù)責(zé)待采集網(wǎng)站列表的清洗及規(guī)整工作,可以根據(jù)實(shí)際需求動(dòng)態(tài)添加處理邏輯。本文主要實(shí)現(xiàn)了兩類清洗工作:一是對(duì)網(wǎng)站URL 的檢查和規(guī)整,二是對(duì)一個(gè)網(wǎng)站包含多個(gè)URL 時(shí)進(jìn)行拆分和填充形成多條記錄。關(guān)鍵實(shí)現(xiàn)代碼如下:
For domain in [domain.strip().replace(‘ ‘, ‘) for domain in line.split(‘;) if domain]:
L i n e . u p d a t e ( { ‘ i d x : s e l f . g e t _ i d x ( ) , d o m a i n :domain,sk_ip: self.get_ip(domain)})
2. 網(wǎng)站響應(yīng)狀態(tài)采集
HttpCodeSpider 模塊通過(guò)Scrapy.ScrapyRequest 發(fā)起Get 請(qǐng)求后,正常狀態(tài)下ScrapyResponse 中Status 即可作為網(wǎng)站響應(yīng)狀態(tài)信息。但當(dāng)網(wǎng)站狀態(tài)異常時(shí)網(wǎng)站響應(yīng)狀態(tài)信息需從異常捕獲函數(shù)中收集。關(guān)鍵代碼實(shí)現(xiàn)如下所示:
Def errback(self, failure):
Cb_kwargs = failure.request.cb_kwargs
I t e m = I n s p e c t o r I t e m ( { ‘ d o m a i n : c b _kwargs[‘domain],idx: cb_kwargs[‘idx],})
Try:
Response = failure.value.response
Item.update({‘status: response.status,})
Except AttributeError:
Item.update({‘status: 700,title: failure.value})
Yield item
3. 域名解析信息采集
ApiSpider 模塊采集的數(shù)據(jù)需要通過(guò)第三方的查詢API 獲取,該API 返回JSON 格式的數(shù)據(jù)結(jié)果。發(fā)起請(qǐng)求時(shí)需在請(qǐng)求頭中指定API code 作為認(rèn)證標(biāo)識(shí),在處理返回的數(shù)據(jù)時(shí)需要記錄查詢成功和失敗的數(shù)據(jù)。具體實(shí)現(xiàn)如下:
If api_ret.setdefault(‘a(chǎn)pi_msg).startswith(‘success):
Wanted_vals = [api_data.setdefault(key) for key in wanted_keys]
Api_ret.update(dict(zip(wanted_keys, wanted_vals)))
Yield InspectorItem(api_ret)
else:
Yield InspectorItem(api_ret)
4. 網(wǎng)站首頁(yè)信息采集
當(dāng)目標(biāo)網(wǎng)站為動(dòng)態(tài)網(wǎng)站時(shí), 普通的Scrapy.ScrapyRequest 無(wú)法獲取準(zhǔn)確的網(wǎng)站首頁(yè)信息,SplashSpider 統(tǒng)一借助Scrapy-Splash 的SplashRequest 接口向Splash 服務(wù)器發(fā)起請(qǐng)求。由Splash 完成目標(biāo)網(wǎng)站的渲染并返回渲染后的網(wǎng)站首頁(yè)Html 和截圖信息。由于需要獲取網(wǎng)站首頁(yè)的全屏截圖,因此需要在發(fā)起請(qǐng)求時(shí)指定Render_all 參數(shù)的值為1。
5. 數(shù)據(jù)持久化存儲(chǔ)
DBhelper 模塊存儲(chǔ)到數(shù)據(jù)庫(kù)中的字段信息如表1所示。
五、數(shù)據(jù)采集結(jié)果
本次測(cè)試中采集的網(wǎng)站數(shù)量為7400 個(gè)。其中網(wǎng)站響應(yīng)狀態(tài)碼為200 的共計(jì)4892 個(gè),即以最為耗時(shí)的網(wǎng)站首頁(yè)信息采集模塊為例,對(duì)網(wǎng)站狀態(tài)該模塊從啟動(dòng)到最終結(jié)束共耗時(shí)5603 秒。累計(jì)采集了4892 個(gè)的網(wǎng)站首頁(yè)標(biāo)題、首頁(yè)文本以及首頁(yè)全屏截圖。
六、結(jié)束語(yǔ)
不同于一般針對(duì)某個(gè)網(wǎng)站進(jìn)行深度的數(shù)據(jù)采集,本文所提出的采集系統(tǒng)主要是解決如何批量采集網(wǎng)站狀態(tài)信息的問(wèn)題。其難點(diǎn)在于待監(jiān)測(cè)的網(wǎng)站數(shù)量龐大、采集的數(shù)據(jù)來(lái)源較為分散、動(dòng)態(tài)網(wǎng)站的數(shù)據(jù)無(wú)法直接通過(guò)一次網(wǎng)絡(luò)請(qǐng)求就完成采集。本文在Scrapy 的基礎(chǔ)上結(jié)合Scrapy-splash、Splash 等技術(shù)手段實(shí)現(xiàn)對(duì)多個(gè)網(wǎng)站狀態(tài)信息的批量采集和動(dòng)態(tài)網(wǎng)站首頁(yè)信息的精確采集,能夠?yàn)楹罄m(xù)網(wǎng)站的日常監(jiān)測(cè)和狀態(tài)分析提供有效支撐。
作者單位:趙鵬 蘇楠 于慧霞 國(guó)家計(jì)算機(jī)網(wǎng)絡(luò)與信息安全管理中心寧夏分中心