您的位置:軟件測(cè)試 > 開源軟件測(cè)試 > 開源功能測(cè)試工具 > Selenium
Selenium+PhantomJS的爬蟲那些事兒
作者:Mr.Kua 發(fā)布時(shí)間:[ 2017/3/7 15:01:00 ] 推薦標(biāo)簽:功能測(cè)試 Selenium 進(jìn)程

  0x00
  近寫爬蟲分析灰色網(wǎng)站,要使用無頭瀏覽器動(dòng)態(tài)加載網(wǎng)頁,使用selenium+PhantomJS, 自己研究的時(shí)候遇到了一些比較有意思的坑,和大家分享一下。
  0x01
  先說一下架構(gòu),在大規(guī)模爬取網(wǎng)頁內(nèi)容的時(shí)候,為了提高性能,降低存儲(chǔ)和計(jì)算開銷,單個(gè)PhantomJS進(jìn)程往往需要連續(xù)處理大量的URL,那么針對(duì)單個(gè)PhantomJS進(jìn)程在連續(xù)處理不同URL的時(shí)候,往往會(huì)出現(xiàn)一些意想不到的問題。
  例如,由于我們需要通過
from selenium import webdriver
d = webdriver.PhantomJS()
d.set_page_load_timeout('10')
d.implicitly_wait('10')
d.get(url)
d.current_url
  這樣的方式記錄給予Phantomjs引擎的原始URL和PhantomJS動(dòng)態(tài)加載后的URL,但是當(dāng)單進(jìn)程PhantomJS大量處理URL時(shí),我們發(fā)現(xiàn)有許多原始URL和動(dòng)態(tài)加載后的URL完全無法對(duì)應(yīng)。這會(huì)是什么原因?qū)е碌哪兀?br />   目前遇到的情況而言,大體分為兩種情況導(dǎo)致:
  網(wǎng)頁內(nèi)部存在onbeforeunload事件調(diào)用使得網(wǎng)頁無法被正常關(guān)閉。
  待訪問資源不可用。
  0x02 onbeforeunload
  我們來看一個(gè)比較有意思的網(wǎng)頁,在爬取到該網(wǎng)頁所在的URL之后,phantomjs”停止了工作“, 所有后續(xù)給phantomjs處理的URL 其調(diào)用current_url返回的URL值都是這個(gè)特殊網(wǎng)頁的URL,以下是該網(wǎng)頁內(nèi)含部分HTML代碼:
  <BODY onbeforeunload="return('你確定仔細(xì)閱讀此文章了嗎?')" style="margin:0px;"><iframe border="0" name="lantk"  width="0" height="0" allowtransparency="" scrollbars="yes" frameborder="0"></iframe>
  形如 <ELEMENT onbeforeunload="handler"> 的代碼會(huì)注冊(cè)一個(gè)事件,當(dāng)瀏覽器引擎卸載當(dāng)前HTML文檔之前拋出一個(gè)對(duì)話框,用戶可以確認(rèn)是否他要離開這個(gè)網(wǎng)頁。 對(duì)應(yīng)在Javascript中,可相應(yīng)的對(duì)該事件進(jìn)行處理:
  object.onbeforeunload = handler;
  object.addEventListener("beforeunload", handler, useCapture);
  事件是DOM事件的簡稱,根據(jù)w3.org DOM的文檔 1.4.2 Complete list of event types:unload事件類型的實(shí)現(xiàn)將從環(huán)境中移除該網(wǎng)頁文檔自身和其附帶的所有資源,包括圖片、CSS、和Javascript腳本。在運(yùn)行這樣的事件之后文檔將被卸載。
  那DOM的設(shè)計(jì)者們?cè)O(shè)計(jì)onbeforeunload事件的本意是什么?onbeforeunload存在的意義在于,你無法通過javascript腳本形式對(duì)其進(jìn)行處理,如果用戶想要離開某個(gè)網(wǎng)頁,那么你無法阻止他離開這個(gè)網(wǎng)頁,你也無法在用戶點(diǎn)擊關(guān)閉網(wǎng)頁(觸發(fā)unload事件時(shí))進(jìn)行其他阻止用戶離開的活動(dòng),為了安全性考慮,用戶的自由是第一位的,網(wǎng)頁編寫者無法將用戶”囚禁“在網(wǎng)頁應(yīng)用程序中。
  在IE瀏覽器中,可以通過自定義字段來建立自定義消息,如上述代碼中的 return(‘你確定仔細(xì)閱讀此文章了嗎?' ,而在其他瀏覽器中,這個(gè)事件不會(huì)顯示自定義的消息。
  那么在我們的案例中,原因在于phantomjs接收到URL并加載了網(wǎng)頁DOM之后,受困于onbeforeunload事件,導(dǎo)致代碼中沒有拋出異常,然而引擎也永遠(yuǎn)停留在了當(dāng)前頁面,對(duì)于后續(xù)接收到的任務(wù)不予處理。
  URL無法訪問的其他情況
  除了上述的情況導(dǎo)致phantomjs異常以外,還有一種由于URL無法訪問導(dǎo)致current_url異常的情況,這種情況可以在如下三種子條件下觸發(fā):
  DNS查詢返回域名不存在,無論是本地hosts還是遠(yuǎn)程DNS服務(wù)器的返回都可以。
  URL的無法返回正常HTTP響應(yīng)。
  單獨(dú)通過phantomjs訪問上述兩種情況的URL,current_url將會(huì)返回 about:blank , 然而如果同一phantomjs進(jìn)程曾經(jīng)處理過其他URL, 則由于上述兩種情況,phantomjs driver沒有真正去處理第二個(gè)URL。
  我們可以把phantomjs的狀態(tài)簡單的視為一個(gè)狀態(tài)機(jī)模型,由于在處理一系列URL時(shí)候狀態(tài)是連續(xù)傳遞下去的,通過輸入,會(huì)改變狀態(tài)機(jī)的狀態(tài),而滿足上述任意一個(gè)條件的輸入,將導(dǎo)致phantomjs狀態(tài)停留在原位,當(dāng)用戶簡單將輸入和輸出進(jìn)行對(duì)應(yīng)的時(shí)候會(huì)出現(xiàn)問題。
  0x03 解決方案
  針對(duì)onbeforeunload的問題,可以在獲取網(wǎng)頁源碼后加入對(duì)使用onbeforeunload的網(wǎng)頁使用phantomjs自身對(duì)網(wǎng)頁中注入js腳本進(jìn)行處理。然而這樣會(huì)造成一定的性能下降。
  針對(duì)URL無法訪問的情況,有兩種方案: 1.可以在phantomjs處理每個(gè)任務(wù)之前先進(jìn)行DNS查詢,對(duì)于無DNS解析記錄的可以不用提交phantomjs處理。然而這種方案對(duì)于IP類URL沒有很好的控制。 2。可以在phantomjs每個(gè)任務(wù)之間插入 driver.get('http://about:blank') 這樣可以間接避免上一個(gè)URL的狀態(tài)污染到下一個(gè)任務(wù)。
  然而,這兩種方法都是治標(biāo)不治本,根本解決方案應(yīng)該是將每phantomjs會(huì)話之間的執(zhí)行完全隔離,我們使用selenium連接phantomjs后端其實(shí)使用的是detro開發(fā)的ghostdriver,是一個(gè)Remote WebDriver Wire protocol的Phantomjs實(shí)現(xiàn),根據(jù) Github上的鏈接 ,以及作者ghostdriver庫github頁首的簡介,項(xiàng)目開發(fā)者由于生活的壓力已經(jīng)兩年沒有更新了。。你們說Github是不是應(yīng)該開一個(gè)打賞機(jī)制?

軟件測(cè)試工具 | 聯(lián)系我們 | 投訴建議 | 誠聘英才 | 申請(qǐng)使用列表 | 網(wǎng)站地圖
滬ICP備07036474 2003-2017 版權(quán)所有 上海澤眾軟件科技有限公司 Shanghai ZeZhong Software Co.,Ltd