席文強
摘 要 當今開源瀏覽器引擎中,Web Kit與gecko平分秋色,但對于代碼結構框架,Web Kit似乎要清晰很多,因此,對于網絡爬蟲研究來說,Web Kit的移植研究顯得尤為重要。本文通過對Web Kit瀏覽器內核移植目的的闡述,分析了移植過程中的各個步驟以及問題改進方法,對獲取網頁鏈接節點和實現步驟做了分析和研究。
關鍵詞 Web Kit;JS;Web Core;網絡爬蟲
引言
1移植的目的
引擎的爬蟲需要從給定的url中抽出盡可能多的正確鏈接,這不僅需要獲取到頁面源碼中顯式的url,還需要解析js,模擬一些事件來得到那些“隱藏”的鏈接。Web Kit可以滿足這個需要。并且,為了更方便地為引擎所用,減少引擎的體積,采用了自己移植的方式。
Web Kit的移植可以分為:組建工程,實現接口,封裝接口。對于不基于任何一個移植(如qt,gtk等)的移植,要做的工作非常多。但對于現在的需求,很多功能都是不需要的,這就簡化了很多的工作量。對于必須要實現的接口,也都做的盡量簡單[1]。
2移植工程
在移植的很多時候要參考別的平臺的移植,要把每個平臺的都看一下,每一個地方可能要參考不同的平臺。這個地方可以參考efl的。
在各個相應的目錄下建立PlatformXXX.cmake或OptionXXX.cmake。OptionXXX.cmake里包含了編譯之前需要的一些信息,如編譯選項的打開關閉等。在移植中,可以先把所有的關閉,通過字面意思看出需要打開的先打開,在運行時遇到問題時,要記住這邊的選擇,排查問題時不要忘記這邊的影響。
PlatformXXX.cmake里包含要編譯的源文件,依賴的庫等。依賴的庫先選最小集,需要時添加。源文件參考其他移植,對于平臺相關的源文件,先不要加入。
這個步驟并不是一步完成的,實際上各個步驟都是穿插的,后面編譯或運行時遇到的問題,都可能要回到這里修改配置文件。
2.1 實現平臺相關的接口
在編譯時,根據錯誤建立相應的目錄和文件,并在PlatformXXX.cmake中添加新建的源文件。這里必須要注意的一點是,接口先定義成空實現,并在里面打印相應的信息。這樣可以先通過編譯,但在后面運行時可能會報錯。而報錯時就可以根據打印的信息確定是什么原因導致,從而實現相應的接口以使運行正常。
這些接口Web Kit之所以空出,目的就是方便各個平臺的移植。對于一些平臺相關的接口,Web Kit采用的方式是建立一個托管類,這個托管類中都是一些純虛函數。這樣,各個平臺在做自己的移植時,就可以建立子類繼承這個托管類,并實現其中的純虛函數。還有另外一種方式,就是空出未實現的接口,由移植時實現。
FrameLoaderClientXXX.cpp中有很多函數會在加載的過程中被調用,在其中完成一些與外界的交互后,再返過去調回到Web Core內部。如果在程序執行時某個函數內中斷,可以參考其他移植如qt,明確接口的含義,以及需要回調的函數,再具體實現[2]。
2.2 調用并封裝接口
調用內部的接口使Web Kit運行起來。如初始化、加載頁面、執行動作等。首先要定義Web View和Web Frame,Web View對應Web Core內的Page,在其中完成構造,初始化等,以及與外界的交互(提供供python調用的類和接口)。Web Frame對應Web Core內的Frame類,在這里開始頁面的加載,數據,配置的傳入等。
另外,對于傳給Web Kit的數據和配置,以及Web Kit報出的信息,由于需要在Web Core內使用,則要在WTF內定義。
3各模塊移植時的改動
Web Kit移植時需要對各個瀏覽器內核模塊進行修改,以便于節省計算資源,對無用模塊進行裁剪,同時添加回調空函數以保證有效性和可用性。
3.1 WTF模塊
WTF模塊這里放的都是Web Kit內部定義的一些庫供其他模塊使用,算是最下層的,編譯的時候當然也是最先編譯。里面有智能指針,各種容器,hashmap,內存分配,線程等管理的接口。其中有Platform.h文件,里面是一些宏的定義,移植的時候需要稍作修改。
一些需要傳給Web Kit的配置信息,以及與外部與Web Kit交互的數據,其類的定義也要在這個模塊下。因為WTF是最先編譯的,而Web Core和Web Kit都有可能會用到那些結構。
3.2 Javascript Core模塊
JS引擎在移植的時候幾乎不需要動。但有部分內存是分配在Heap中的,jscore中的垃圾回收,如果想實時回收,則需要在這里實現那些基于定時器的接口。如果要求不高,可以在外部通過調用Web Core內提供的接口回收,GCController中的garbage CollectNow()等,但這個起的作用非常有限[3]。
3.3 Web Core模塊
這部分最需要說的是定時器Timer。Web Kit中Timer實現的基本思想是:每個線程維護一個虛擬Timer的優先級隊列,每次啟動或停止一個虛擬Timer時,都會設置該Timer的下次觸發時間(“next fire time”)。當虛擬Timer的觸發時間變化時,需要調整其在優先級隊列的位置,以保證隊列的有效性。當虛擬Timer啟動或者先前的系統Timer觸發的時候,會調用由具體平臺實現的set Shared Timer Fire Time函數,去設置系統Timer的等待時間,開始新一輪的超時等待。
其中,Timer是個模板類,繼承于Timer Base,它就是所謂的虛擬定時器,提供了具體平臺的系統Timer觸發時的回調接口,以及啟動和停止定時器的相關接口。另一方面,Timer Base提供了優先級隊列(由堆結構實現)的訪問接口,例如:heap Decrease Key用于調整堆,以保證當前定時器的下次觸發時間縮短后優先級仍然有效;heap Delete從優先級隊列中刪除當前定時器;heap Insert向優先級隊列插入定時器。Timer Base中提供的優先級訪問接口直接操縱的是Thread Timers的m_timerheap成員。
因此定時器Timer一定要實現一個符合要求的。簡單的定時器,比如基于時鐘的信號,表面上能正常工作,但是會隱藏很多問題。比如多線程,一些段錯誤,還有內存泄漏。
3.4 Web Kit接口層
在這里,要構造兩個重要的對象,Page和Frame,以及相關的一些對象。要進行一些必要的初始化,可以根據錯誤提示進行,也可以參考其他的移植[4]。
3.5 段錯誤問題
基于源碼移植的Web Kit,跑起來肯定會有很多段錯誤??梢杂胓db調試。例如ASSERT FAILED:這類段錯誤比較常見。Web Kit為了方便排查問題,盡量在錯誤犯下之后最早的時候通過這種方式給出提示。也就是說,這個段錯誤是Web Kit故意造成的,但造成的原因是Web Kit發現了異常的東西。未初始化:如果提示cannot access memory at 0x….,那有可能是這個原因。這時要考慮是不是在Web Kit移植層,該創建的對象沒有創建。
參考文獻
[1] 詹恒飛,楊岳湘,方宏.Nutch分布式網絡爬蟲研究與優化[J].計算機科學與探索,2011,5(1):68-74.
[2] 胡戎,馮仲科,蔣君志偉.基于CheerIO的MEAN Stack氣象數據網絡爬蟲研究[J].農業機械學報,2016(6):275-282.
[3] 劉高軍,夏景隆.基于Heritrix的網絡爬蟲研究與應用[J].軟件導刊,2013,12(5):123-125.
[4] 賈海軍.基于URL及上下文的主題網絡爬蟲研究[D].上海:上海師范大學,2014.