趙鵬 蘇楠 于慧霞



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