朱延剛
(南京信息工程大學,江蘇 南京 210044))
隨著互聯網技術的迅速發展,各類互聯網平臺大量涌現。 但這些平臺在為網絡用戶提供豐富的媒體資源的同時,也增加了用戶直接高效獲取有用信息的難度,特別是具有行業性、專業性的信息,很容易淹沒在信息的海洋中。 僅僅通過傳統的搜索引擎獲取的檢索信息,已經無法滿足更為專業的信息需求。 因此,如何對海量的信息進行歸納和提取變得十分重要。 目前,有很多針對特定場景設計的Java 爬蟲系統,可以滿足如圖片下載[1]、特定技術主題或新聞媒體咨詢整合等多種特定需求[2-4]。 這些爬蟲系統的提出,都能針對特定的業務場景設計出針對性比較強的解決方案。 設計一種適用性和可擴展性更好的爬蟲信息采集系統,不僅可以降低用戶構建特定場景的爬蟲系統設計難度,還能提高設計者的開發效率。 本文基于WebMagic 框架,提出了一種適用性和可擴展性更好的開發框架。
HttpClient 是Apache HttpComponentsTM項目負責創建和維護的一個基于HTTP 協議的Java 組件開發包。 HttpClient 與一般的瀏覽器不同,它不提供UI 界面,但是可以完成和瀏覽器相同的功能。 此外,還可以用來發送請求,接收服務器響應數據。 因此,HttpClient 通常被開發人員作為API 來調用。 Jsoup是一個用于處理HTML 的Java 庫,可以提供一個非常便捷的API。 在網頁爬蟲中,Jsoup 主要用來從URL、文件或字符串中抓取和解析HTML,使用DOM 遍歷或CSS 選擇器查找和提取數據,操作 HTML 元素、屬性和文本。 除此之外,它還能根據安全列表清理用戶提交的內容,以防止 XSS 攻擊。
SpringData 是被廣泛使用的Web 開發框架Spring的重要組成部分,用于簡化數據庫的訪問。 本文之所以介紹SpringData,是因為它具有強大的數據持久層的支持開發能力。 不僅支持關系型數據庫的數據存儲,比如JDBC,JPA 等,也支持非關系型的數據存儲,比如Redis[5],ElasticSearch[6],Neo4j[7]等。 SpringData框架很好地提升了數據存儲層面的適用性和可擴展性。
目前,主流的Java 爬蟲框架主要有Nutch,Crawler4j,WebMagic,WebCollector 等。 這些爬蟲框架都有各自的優缺點。 本文選擇WebMagic 作為基本的爬蟲框架,主要基于以下3 點:(1)WebMagic 的設計參考了業界比較成熟的爬蟲框架 Scrapy;(2)WebMagic 支持多線程任務,能充分利用硬件資源,提高數據的處理速率;(3)WebMagic 的4 大組件Downloader,PageProcessor,Scheduler,Pipeline 對應了爬蟲生命周期中的下載、處理、管理和持久化4 個過程。 對系統開發者來說,主要的業務代碼通常在PageProcessor 完 成 即 可。 其 他 的3 大 組 件, 即Downloader,Scheduler,Pipeline,也具有定制性。 系統設計者可以根據自己的需求,來修改完善組件的代碼。 因此,選擇WebMagic 框架作為基礎架構能夠使整個系統具有更好的適應性和可用性。
本文提出的基于WebMagic 的爬蟲框架,主要包含了3 個業務處理流程:頁面數據獲取、數據分析處理和數據存儲持久化。 每個業務流程也會提交或者產生不同階段的數據。 通過分析這些數據流,能促進系統設計者更好地理解系統結構,了解需要參與的實體,以及每個業務流程在每個階段需要完成的明確任務和目標。 具體的結構如圖1 所示。 本文將具體闡述每個框架結構部分,具體的設計思路。
對于頁面數據獲取而言,系統設計者在設計任何場景的網頁爬蟲系統時,都需要確定目標信息定位,明確將哪些站點平臺作為信息來源,其選取的信息源的質量越高,得到的信息就越有價值。 因此系統設計者是否能選取合適的站點,對于獲取數據頁面信息是至關重要的。 一般網頁獲取分為開放式和注冊登錄式。 前者的瀏覽權限比較低,使用者通過訪客模式就可以正常瀏覽站點信息,這種類型的頁面數據獲取比較容易。 相比之下,登錄注冊式的站點更加復雜,信息獲取的流程更為煩瑣。 本文針對登錄注冊式站點的頁面數據獲取流程做了簡要闡述。
對于登錄注冊式的頁面,用戶可以進行常規的注冊,然后獲取用戶名和密碼,再通過框架的API 接口,模擬瀏覽器登錄。 對于帶有圖片驗證的模擬登錄,其登錄過程也是目前的一個難點。 對于站點的分類分頁頁面數據,系統設計者還要通過瀏覽器的開發者工具,分析HTTP 報文的請求頭和響應頭以及響應參數的特點。 設計者對請求頭的請求參數進行分析可以幫助其確定需要提交哪些請求參數。 設計者通過點擊分頁發起請求時,對應的請求參數需要攜帶Cookie中的JSESSIONID,而當設計者對這一請求所產生的響應數據進行分析時,有時候可以得到一些JSON 字符串,也就是設計者需要提取的信息文本。
系統設計者獲取了頁面的數據之后,需要進一步地對頁面數據進行分析處理。 這里的頁面數據的主要形式就是服務器響應給請求端的HTML 標簽,CSS樣式表等字符源碼。 為了更好地定位到需要的頁面標簽數據,系統設計者同樣可以借助瀏覽器的開發者工具,通過點擊元素檢查按鈕,迅速定位標簽,提升數據分析效率。 在確定了具體的元素標簽之后,系統設計者就可以借助WebMagic 3 種數據抽取技術(即XPath、CSS 選擇器和正則表達式)對需要的頁面數據進行分割提取,或者選擇相對獨立的Jsoup 開發庫作為數據抽取工具。
基于WebMagic 的爬蟲框架有一個比較大的優點,就是支持多線程任務。 系統設計者可以充分利用開發平臺的硬件性能,減少數據抓取處理的時間,但是需要注意線程的安全性問題。 WebMagic 會自動提取頁面出現的HTTP 的請求連接,然后把它們放到請求的隊列中去。 如果有多個分類,每個分類還有多個頁面,而且每個分類頁面數據返回的時間也具有不確定性。 如果系統設計者在同一個PageProcessor 里處理業務邏輯,就要考慮多線程條件下的線程安全問題,否則將無法保證結果數據的準確可靠。
系統設計者在獲得了目標數據之后,為了便于后期進一步對數據進行分析提取,就要解決數據的存儲問題。 目前,數據存儲主要有兩種方案:關系型數據持久方案和非關系型數據持久方案。 系統設計者最終選取何種數據存儲方案更合適,需要根據自身的業務需求和特點并結合下一步對數據處理需要采取的具體策略來確定。 關系型數據持久方案的優點是易于維護,支持通用的SQL 查詢語句,且支持事務處理。但它也有缺點,即讀寫較慢,不適合對I/O 要求高的操作。 非關系型數據持久方案存儲數據的結構靈活,還支持內存存儲,所以讀寫性能較好,查詢速度快,但它的缺點是不支持通用的SQL 查詢,基本也不支持事務處理,且對于初學者來說,其學習成本也更高。
基于WebMagic 的爬蟲框架使用了Pipeline 數據持久化組件,并提供了3 個實現類:用于向控制臺輸出的ConsolePipeline 類,用于向磁盤輸出文件的FilePipeline 類,還有用于保存Json 格式文件的JsonFilePipeline 類。 系統設計者可以根據需要定制合適的Pipeline,以實現對數據的持久化存儲。 系統設計者可以把Pipeline 作為一個可選的組件,因為WebMagic 作為一種Java 開發平臺本身就擁有很好的靈活性,設計者可以使用個性化的數據持久化組件,例如,設計者可以把本文技術介紹部分提到的SpringData 框架作為組件,從而發揮該框架支持的多種數據類型的持久化功能。 系統設計者可以進一步提升系統的適用性和可擴展性以及對抓取數據后期的可用性。 這些系統提升有利于對數據進行更深層次的處理。
基于WebMagic 爬蟲框架的系統設計中,一般不可缺少的組件是PageProcessor。 通過這個組件,系統設計者可以對頁面數據分析處理邏輯進行設計,而具體的邏輯設計需要根據具體的業務需求來確定。PageProcessor 組件也定義實現了一些具有HttpClient功能的對象,比如Site 對象。 站點本身的一些配置信息,例如編碼、HTTP 頭、超時時間、重試策略和代理等,都可以通過設置Site 對象來進行配置,增強框架的功能。 下面展示了一個帶分頁的爬蟲數據分析提取的例子,部分核心代碼如下:
class DemoProcessor implements PageProcessor {
/ /站點對象參數設置
private Site site = Site.me()
.setCharset("UTF-8")/ /設置編碼
.setTimeOut(10000)/ /超時時間
.setRetrySleepTime(3000)/ /重試時間
.setSleepTime(10);/ /重試次數
public Site getSite() {return site;}
public void process(Page page) {
/ /編寫頁面數據分析處理代碼
/ /獲取頁面上的HTTP 鏈接
String pageUrl = page.getUrl().toString();
/ /將頁面的HTML 標簽通過Jsoup 生成一個可解析的文檔
Document doc = Jsoup. parse(page. getHtml().toString());
/ /借助開發者工具定位標簽元素解析數據
String div = doc.select("div[module-name=icbupc-productListPc]").attr("module-data");
/ /對數據進行URLDecode 解碼
String decode = URLDecoder.decode(div);
/ /將字符串轉化成JSON 對象進行解析
JSONObject jsonObject =JSON. parseObject(decode);
/ /獲得當前分類是數據總數目
Integer totalLines = jsonObject. getJSONObject("mds")
. getJSONObject(" pageNavView"). getInteger("totalLines");
/ /獲取當前分頁的頁碼
Integer pageLines = jsonObject. getJSONObject("mds")
. getJSONObject(" pageNavView"). getInteger("pageLines");
/ /獲取當前分類的總頁數
int totalPage = (totalLines + pageLines - 1) /pageLines;
}}
Spider 對象是爬蟲啟動的入口對象。 在這個入口對象create 方法的內部,系統設計者可以傳入完成的頁面數據爬取業務實現類DemoProcessor,然后再調用run 方法進行啟動。 此外,另一個在Spider 對象上比較常用的方法是addUrl,系統設計者可以用這個方法來添加初始的URL 地址參數。 該地址參數一般就是站點的入口地址。 Spider 對象的部分核心代碼如下:
Spider. create (newDemoProcessor ( ))/ /傳入PageProcessor 參數
.addUrl(URL)/ /初始的URL
.thread(10)/ /開啟的線程數
.run();
Spider 對象還提供一個addPipeline 方法,顧名思義,這個方法主要是用于傳入設計人員自定義的Pipeline 組件對象的。
關于如何把獲取的提取數據進行持久化的問題,本文在框架結構設計部分已經做了闡述。 如果系統設計者將得到的目標數據保存成文件的形式,則不利于大數據量的管理和查詢。 對此,本文認為,系統設計者需要對數據進行數據庫的持久化設計。
3.3.1 通過定制Pipeline 的方式
通過Pipeline 有兩種導入數據庫的方式:注解方式和常規的代碼方式。 系統設計者可以定義一個名為AnnotationObjectDaoPipeline 的注解類,具體代碼如下:
@Component("AnnotationObjectDaoPipeline")
public class AnnotationObjectDaoPipeline implements PageModelPipeline
注 解 的 方 式 需 要 實 現 WebMagic 的PageModelPipeline 接口。 系統設計者通過泛型傳入定義好的數據模型,最后在內部實現process 方法即可。
另一種是常規代碼的實現方式。 系統設計者需要定義一個實現類CommonCodePipeline,具體代碼如下:
public class CommonCodePipelineimplements Pipeline {}
這種方式也需要系統設計者重寫內部的process方法。 兩種方式可以根據設計者自身的需求,靈活應用。 Pipeline 其實就是將PageProcessor 的抽取結果,進行獨立的處理。 之所以設計者可以考慮使用Pipeline 組件,主要是因為其具有兩大優點:(1)這種方式實現了模塊分離,代碼結構比較清晰,而且解析抽取和數據保存各自占用獨立的線程,互不干擾;(2)Pipeline 的功能比較固定,更容易做成通用組件。
3.3.2 自定義數據持久化接口
如果系統設計者認為使用Pipeline 組件比較煩瑣,學習成本較高,也可以設計定義常規的數據接口層。 具體而言,設計者可以根據業務需求設計Service層和Dao 層,然后直接在PageProcessor 組件內部使用process 方法,增加數據保存的業務代碼。 這種方式的優勢是降低了學習成本,提高了系統設計開發的效率,但其缺點是增加了系統模塊的耦合性,不利于后期的代碼維護。
針對一般網頁爬蟲系統設計具有的特殊性和場景單一性,本文提出了一種基于WebMagic 框架的具有可適用性和可擴展性的系統設計。 系統設計人員若采用這一系統的設計方案,可以更好地搭建自己的爬蟲項目,構建可用性和健壯性更好的數據系統。 本文提出的系統仍存在較大的可完善空間,比如設計者可以繼續優化增量爬蟲數據的備份以及解決去重問題等,以進一步提升系統功能的完善性。