廖雨寒 楊彥松 張斌
一、背景
隨著網絡的迅速發展,萬維網成為大量信息的載體,如何有效地提取并利用這些信息成為一個巨大的挑戰,網絡爬蟲(web crawler)隨之而生。但是現在的網站很少有純靜態網頁,大部分網站都通過JavaScript渲染、ajax異步等實現網頁數據加載。對于目前的爬蟲框架來說,基本都是爬取到的未渲染過的HTML源碼,所以對于爬蟲來說沒真正做到瀏覽器的所見即所爬。對于scrapy來說,官方有一個scrapy-splash項目支持頁面渲染解析,然而scrapy-splash在高并發狀態下極其不穩定。一種方案是通過webkit瀏覽器引擎直接渲染,另一種方案是通過調用瀏覽器渲染。通過幾種方案比較,最終選擇了Google Chrome Devtools Protocol開發渲染功能。
二、渲染方案可行性分析
1.Scrapy-Splash
Scrapy-splash是scrapy官方團隊提供的一個解決js渲染問題的方案。Splash是處理網頁渲染的模塊,它內部使用的開源的webkit瀏覽器引擎,通過HTTP API來使用渲染服務。在scrapy中通過DownloaderMiddleware處理網頁請求,實際是去請求splash接口并得到渲染后的數據。
Splash優點:
可以并行處理多個網頁
獲取HTML結果和/或截取屏幕截圖
關掉加載圖片或使用 Adblock Plus規則使得渲染速度更快
使用JavaScript處理網頁內容
使用Lua腳本
能夠獲得具體的HAR格式的渲染信息
然而在實際使用scrapy-splash的過程中,也遇到過一些splash的問題:
對于一些特殊網站,并不能很好的處理重定向,比如一些需要通過js渲染Cookie的頁面,需要通過location對象重定向好幾次才能到真正的頁面,但是splash只是處理了第一次的渲染Cookie后并沒有實現跳轉
在并發的情況下,獲取到的數據有時候是沒有內容的
2.Scrapy-QtWebkit
Splash是采用Webkit引擎實現的網頁渲染,可直接采用Webkit對接Scrapy實現渲染網頁。在Qt庫中有相應的QtWebkit模塊,但是在Qt5.6版本以上QtWebkit就被淘汰了,代替它的是QtWebEngine,因此,選擇QtWebKit時,建議使用Qt4或者Qt5.6之前的版本。
Webkit大致通過View-->Page-->Frame的流程來加載網頁。通過這些模塊,對于上面splash遇到重定向問題和空頁面問題都能得到解決。
Webkit優點:
細粒度處理網頁渲染
資源加載可控
直接交與webkit處理更效率
Webkit缺點:
內存資源占用較大
高并發處理網頁過多容易引起C底層錯誤
三、Google Chrome Devtools Protocol
以上兩種方案,都存在各自的缺點, 那有沒有可能直接通過調用瀏覽器加載網頁呢?事實上selenium可以操作瀏覽器。selenium操作chrome是使用chromedriver,chromedriver底層應用的是Chrome Devtools Protocol,因此,何不直接使用Chrome Devtools Protocol。
Chrome Devtools Protocol介紹
在Chrome/Chromium瀏覽器中,按F12會彈出調試工具,它是通過Chrome Devtools Protocol的協議來進行數據通訊。Chrome Devtools Protocol用來與瀏覽器頁面(pages)交互和調試的協議通道。它采用websocket來與頁面建立通信通道,由發送給頁面的Commands和它所產生的Events組成。
在Chrome Devtools Protocol中,有很多不同的功能模塊域(domains),類似于Chrome開發這工具的個功能模塊。
然而對于此例的爬蟲來說,只需要用到Network,Page,Runtime等幾個功能模塊域:
Network允許跟蹤頁面的網絡活動,它公開http, file, data,網絡請求和響應等數據信息
Page會檢查與頁面相關的動作和事件
Runtime主要用作運行JavaScript操作代碼
啟動調試實例
要使用Chrome Devtools Protocol,需要開啟調試。我們的項目運行在服務器中,所以需要開啟無頭模式 --headless --disable-gpu --no-sandbox。
chrome.exe --remote-debugging-port=9222 --headless --disable-gpu --no-sandbox
如果需要遠程調試,可以加上參數--remote-debugging-address='0.0.0.0'。
操作Chrome Devtools Protocol
通過以上命令可以啟動一個Chrome調試實例,通過HTTP調用接口:
http://loacalhost:9222/json
會得到接口返回的JSON數據:
[
{
"description": "",
"devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/A6C1F7B23DFF222A87143ACB37CBF7C4",
"id": "A6C1F7B23DFF222A87143ACB37CBF7C4",
"title": "about:blank",
"type": "page",
"url": "about:blank",
"webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/A6C1F7B23DFF222A87143ACB37CBF7C4"
}
]
默認開啟實例后會有一個空的標簽頁(Tab),要創建新的Tab,只需要通過調用接口
http://loacalhost:9222/json/new
或者在后面直接添加網址,Tab將會創建并加載網頁
http://loacalhost:9222/json/new?http://www.example.com/
在每個Tab中有一個webSocketDebuggerUrl字段,它提供了一個WebSocket接口與Chrome交互。比如要啟用Page模塊域,通過WebSocket發送以下命令開啟:
{"id":1, "method":"Page.enable", "params":{}}
關閉Tab
http://loacalhost:9222/json/close/A6C1F7B23DFF222A87143ACB37CBF7C4
四、Scrapy實現Chrome Protocol下載渲染頁面
Scrapy是一個爬蟲框架,它使用了Twisted異步網絡庫來處理網絡通訊,他的大致架構流程如圖
圖 1 Scrapy框架
在圖1中我們看到scrapy處理網絡請求的是Downloader模塊,他通過DownloaderHandler下載處理器完成下載網絡請求,下載處理器使用的是Twisted網絡庫實現的,對于Chrome Protocol來說我們通過接口操作命令實現網頁加載本質上是Chrome加載網頁,也就是說下載處理是通過Chrome Protocol接口操作Chrome瀏覽器請求加載并渲染網頁,因此我們需要拿到渲染后的網頁HTML源碼,需要改寫DownloaderHandler。
然而原生操作Chrome Protocol太繁瑣,我們需要封裝它。在Github上有多種語言實現了Chrome Devtools Protocol的封裝。對于Python來說,我們使用Pychrome庫來操作Chrome Protocol,可以直接用pip安裝模塊:
pip install pychrome
當然要使用該模塊得要啟動Chrome調試實例,通過實例的地址和端口號連接:
browser = pychrome.Browser(url="http://<your_ip>:9222")
通過new_tab()方法創建tab標簽頁,通過tab.start()啟動當前tab的websocket鏈接,例如需要啟動Network功能域,通過調用tab.Network.enable()來啟動,通過tab.Page.navigate(url="http://www.example.com")加載網頁,該頁面加載時的一切網絡活動都可以通過接收websocket響應得到json數據。
這時會有一個問題,何時才算是真正渲染完成頁面?
圖 2 JS渲染流程
在圖2中是瀏覽器從輸入網址到頁面加載完成的處理流程,loadEventEnd是真正加載完成,但是在一些使用ajax異步請求的網頁使用loadEventEnd并不能很好的判斷為渲染結束,所以這里我們使用JavaScript命令獲取readyState狀態判斷頁面是否加載完成:
document.readyState
readyState一共有五種狀態:
uninitialized - XML 對象被產生,但沒有任何文件被加載
loading - 加載程序進行中,但文件尚未開始解析
loaded - 部分的文件已經加載且進行解析,但對象模型尚未生效
interactive - 僅對已加載的部分文件有效,在此情況下,對象模型是有效但只讀的
complete - 文件已完全加載,代表加載成功
通過document.readyState == complate來判斷數據的加載進度。當加載完成時通過javascript命令獲取渲染后的HTML源碼:
document.documentElement.outerHTML
在這里我們就可以使用Pychrome替代scrapy的twisted下載處理。
五、結語
使用chrome devtools protocol來操作chrome瀏覽器來渲染頁面,能基本完成渲染頁面的需求,一些需要與瀏覽器交互的頁面也大致能使用JavaScript操作命令,重點是使用chrome操作頁面渲染會很占用資源,因此不宜過多的開啟渲染進程,多線程下載盡量重用Tab標簽頁,因為每多創建一個Tab相當于開了一個進程,最后,爬蟲結束時,應該調用相關接口關閉所有Tab,釋放資源。
-
javascript
+關注
關注
0文章
520瀏覽量
53898 -
應用軟件
+關注
關注
0文章
52瀏覽量
9112
發布評論請先 登錄
相關推薦
評論