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