【文智背後的奧祕】系列篇——分佈式爬蟲之WebKit

版權聲明:本文由文智原創文章,轉載請註明出處: 
文章原文連接:https://www.qcloud.com/community/article/139javascript

來源:騰雲閣 https://www.qcloud.com/communitycss

 

引子:

文智平臺是利用並行計算系統和分佈式爬蟲系統,並結合獨特的語義分析技術,爲知足用戶NLP、轉碼、抽取、全網數據抓取等中文語義分析需求的一站式開放平臺。其中分佈式爬蟲系統是獲取海量數據的重要手段,給文智平臺提供了有效的大數據支撐。html

若是簡化網絡爬蟲(Spider)架構,只留下一個模塊,那麼這個模塊就是抓取器Crawler,它在整個Spider架構中就至關於一個嘴巴,這個嘴巴永遠在web的海量數據世界中尋找食物。最簡單的Crawler就是一個實現了HTTP協議的客戶端,它接收一系列URL,向網站發起抓取請求,輸出一系列網頁Page,如圖1所示。

圖1:Crawler的工做過程java

對於一些小的抓取任務,wget就是一個很不錯的選擇,例如學校裏面搞搜索引擎研究,就常用wget或基於wget源碼作修改來知足需求。對單次網頁下載來講,一般大部分時間都消耗在等待對方網站響應上。若是下載的併發量小,機器和帶寬資源就很可貴到充分利用,抓取速度上不去。做爲商業搜索引擎來講,咱們天天抓取數百萬甚至千萬數量級的網頁,那麼使用wget性能就遠遠不能知足需求。所以咱們須要擁有一個高性能、高併發的輕量級抓取器。web

隨着工做的深刻,特別是文智中文語義平臺的提出,對數據的需求更加精細化、多元化,簡單的HTTP抓取已經不能徹底知足需求。瀏覽器

目前,互聯網上頁面的實現方式多種多樣,愈來愈多的站點使用JavaScript部署,例如在http://www.tudou.com/albumcover/Lqfme5hSolM.html 這個頁面中,其中關於劇集列表信息(如圖2所示)就是利用JavaScript技術來填充的,若是想抓取這個信息,傳統的Crawler就無能爲力;有些頁面抓取須要Post信息(登陸等),隨着Ajax技術使用,在抓取先後須要與頁面進行交互,例如一些新聞的評論頁面,其中的評論信息是經過點擊「評論」連接後利用Ajax技術來異步抓取的,這個信息傳統的Crawler也沒法知足抓取需求,例如http://news.sina.com.cn/c/2014-11-26/184331207293.shtml 這個頁面,如圖3所示。服務器

這些現狀都給web頁面的抓取收錄帶來了困難,也對傳統Crawler提出了挑戰。因此對於Crawler來講,除了高性能、高併發的要求外,還有以下需求:網絡

  • 抓取AJAX頁面、模擬網頁操做,進行表單提交;
  • 經過javascript動態實現網頁跳轉;
  • 對內嵌frame頁進行抓取拼接;
  • 多媒體文檔:音、視頻、圖片等內容的抓取;


圖2:經過Js技術填充的劇集列表信息架構


圖3:經過Ajax異步加載的評論信息併發

這些數據就是海量數據世界中的更美味的食物,而美味的食物老是包裹着厚實的外殼。因此Crawler必須擁有強大的牙齒來破殼取食,而這個牙齒對於Crawler來講就是WebKit。

一.Webkit簡介

WebKit是由Apple公司開發的開源瀏覽器內核,WebKit的發展具體可見文檔[1],這裏再也不贅述。WebKit主要分爲三個模塊:WebCore、JavaScriptCore、平臺應用相關Port。WebCore是最核心的部分,負責HTML、CSS的解析和頁面佈局渲染,JavaScriptCore負責JavaScript腳本的解析執行,經過bindings技術和WebCore進行交互,Port部分的代碼結合上層應用,封裝WebCore的行爲爲上層應用提供API來使用,如圖4所示。

圖4:WebKit框架

一個網頁的加載過程從用戶請求一個URL開始,首先判斷是否有本地cache資源可用,若是沒有則經過platform/network調用平臺相關的下載模塊完成HTML和其餘資源的下載,HTML字符串通過HTML解析器生成HTML DOM樹,並將每一個DOM節點註冊爲JavaScript Object供JS腳本調用,在生成DOM樹每一個節點的同時,同步生成Layout樹的每一個節點,其中保存了佈局信息,和CSS樣式信息,系統繪製時觸發page模塊中的Paint操做,使用platform/graphics調用平臺相關的圖形庫完成實際繪製,整個過程如圖5所示。本文對WebKit內核不作不少介紹,若是感興趣,請參考技術文檔[2]。


圖5:WebKit加載網頁過程

二.WebKit編譯以及裁剪

Spider這裏使用的是Qt中集成的WebKit,所用Qt的版本是Qt-4.7.4中的通用版本,下載地址見文檔[3]。WebKit所在位置爲qt-everywhere-opensource-src-4.7.4/src/3rdparty/webkit。這裏選擇的是單獨編譯QtWebKit。經過QMAKE命令編譯產生MakeFile文件。編譯過程是在接觸過的源碼中屬於比較難編譯的,須要注意的是QtWebKit依賴QtScript,單獨編譯QtWebKit的話,須要單獨編譯QtScript,具體的編譯過程參考文檔[4]。

因爲Spider不須要最終渲染出網頁,只須要WebKit執行以後的網頁內容。同時爲了提升WebKit的執行速度(爬蟲對於性能的要求很是高),這裏對WebKit進行了一些裁剪。裁剪包括去除SVG以及一些可選組件和去除WebKit的渲染網頁(Render和Layout)的過程。

其中WebKit中的可選組件包括對DATABASE的支持組建、對ICONDATA的支持組建、XPATH、XSLT、XBL和SVG的支持組件。這些組件再也不一一介紹,有興趣的能夠Google之。組件的裁剪過程比較簡單,經過修改編譯使用的PRO文件來進行,例如裁剪掉SVG組件,只須要找到WebCore目錄下的WebCore.pro文件,將其中的「qt-port: !contains(DEFINES, ENABLE_SVG=.): DEFINES += ENABLE_SVG=1」修改成「ENABLE_SVG=0」,而後使用qmake生成新的makefile編譯便可。

去除WebKit的渲染和排版(Render和Layout)的過程比較繁瑣,首先須要肯定WebKit中進行頁面繪製和渲染的入口,經過閱讀源碼和GDB調試得知:FrameView::layout操做實現繪製前的排版工做,文檔繪製的入口是Frame::paint函數。通過分析驗證,頁面顯示過程當中的繪製(paint)的函數入口就是Frame::paint,它的繪製動做的觸發來自於上層的動做。做爲最靠近Qt的函數入口,只須要把這個函數註釋掉,全部的繪製動做就不會再發生。layout的動做是因爲FrameView的layout動做引發的。註釋掉函數Frame::paintFrameView::layout以後,就堵住了絕大多數的繪製和排版動做,從而節省了WebKit加載網頁的時間。

三.WebKit在Spider中的應用

如前所述,WebKit爲Spider提供了更強大的數據抓取的能力,其中它做爲一個單獨的服務模塊來處理須要WebKit加載的頁面,目前採用比較簡單的CGI接口來與上游服務對接,與上游服務模塊之間經過HTTP協議進行交互。後期隨着業務複雜度的提高和接口數據的複雜化,不排除使用自定義協議的可能,服務模型如圖6所示。


圖6:WebKit CGI服務

爲了使WebKit做爲一個類庫應用於服務器的運行中,首要的問題就是去除WebKit中全部關Qt圖形化的部分,而後才能夠考慮去掉WebKit中有關Qt的其餘工具類的應用。這樣纔可以在在非圖形化的方式下得到頁面Load以後的內容,而這一內容同時也包括了頁面中的非交互式JS代碼所生成的內容。本文檔所描述的去圖形化步驟爲:

  1. 去除WebKit中全部有關QWidget的代碼;
  2. 在去除了QWidget的基礎上,修改WebKit代碼中有關QWidget屬性的獲取和設置部分;
  3. 去除WebKit中有關QApplication的相關代碼。

可是目前存在的問題是QApplication必須在main函數中初始化並使用的,而經過Qt的文檔也能夠看出每個GUI Qt程序都必須初始化一個QApplication對象,該對象主要管理整個Qt程序的資源以及處理分發Qt程序運行中的事件。這種應用模式是不可以知足做爲一個獨立類庫來使用的,由於QCoreApplication只能在main函數中初始化,而且必須調用app.exec()纔可以進入事件處理的循環。目前只有搞清楚WebKit中的整個執行流程,徹底去除Qt,這一方法須要瞭解整個WebKit中的功能,搞清楚目錄WebKit/qt、目錄WebCore/platform中全部有關文件中的Qt部分的功能,以及與WebCore和JavaScriptCore結合的方式。這種方法優點是能夠完成一個獨立的類庫,未來的服務器運行效率要高,劣勢是須要人力和時間去研究上述代碼,時間週期長,因此目前仍是保留app.exec()

因爲Spider下載須要考慮外網權限和網站封禁等策略,這裏使用重寫QNetworkProxyFactory類中queryProxy來實現網絡代理,首先配置能夠訪問外網的機器列表,經過對URL串計算MD5值,而後根據MD5值計算hash值,以決定使用哪臺外網機器來下載數據。

WebKit不只會加載URL對應的HTML文檔,同時會下載HTML文檔中的那些圖片數據以及CSS、JS數據等。可是對於Spider來講,目的是可以發現更多的優質URL,對於網頁渲染的樣式和圖片數據並不關心,因此下載這些數據對於Spider來講是一種額外的負擔。這裏經過對QNetworkAccessManager中的createRequest進行重寫,對於後綴是css、png、gif、jpg、flv的URL返回一個不可到達的request,這個request直接返回一個錯誤,並不會發起真正的網絡請求,這樣就減小了網絡IO,加快網頁的加載速度.

目前基於WebKit,Spider實現了抓取AJAX網頁、模擬點擊後抓取需求。抓取AJAX頁面比較簡單,WebKit在load網頁以後,會執行頁面中JS腳本,實現異步拉取數據,而後從新拼裝頁面,webframe在收到loadfinsh信號以後,便可得到加載異步數據以後的頁面。模擬點擊也比較相似,經過JS代碼嵌入到網頁中,而後經過evaluateJavaScript函數觸發JS代碼執行,執行完再獲取網頁數據便可。目前正在開發支持JS實現網頁跳轉(通常瀏覽器訪問一條URL發生跳轉時,地址欄的URL會改變,捕獲到這種改變,即能拿到全部跳轉的URL。在應用層監聽QWebFrame的urlChanged信號,當地址欄的URL發生改變時觸發自定義的onUrlChanged槽函數,經過這個槽函數來實現自動跳轉,得到跳轉後的頁面)、支持多協議抓取等功能,相信WebKit會在Spider中的應用愈來愈普遍,可以爬取更多殼內的寶貴數據。

參考文檔:
[1] http://web.appstorm.net/general/opinion/the-histoy-of-webkit
[2] https://www.webkit.org/coding/technical-articles.html
[3] http://download.qt-project.org/archive/qt/4.7/qt-everywhere-opensource-src-4.7.4.tar.gz
[4] http://trac.webkit.org/wiki/BuildingQtOnLinux

相關文章
相關標籤/搜索