盤點selenium phantomJS使用的坑

說到python爬蟲,剛開始主要用urllib庫,雖然接口比較繁瑣,但也能實現基本功能。等見識了requests庫的威力后,便放棄urllib庫,并且也不打算回去了。但對一些動態加載的網站,經常要先分析請求,再用requests模擬,比較麻煩。直到遇到了selenium庫,才發現爬動態網頁也可以這么簡單,果斷入坑!

selenium是python的一個第三方自動化測試庫,雖然是測試庫,卻也非常適合用來寫爬蟲,而phantomJS是其子包webdriver下面的一個瀏覽器。phantomJS本身是一個無頭瀏覽器(headless browser),也稱無界面瀏覽器。可以在通過官網下載運行phantomjs.exe,簡單幾行代碼也能訪問網頁,爬取數據。但本文主要討論通過python的selenium庫使用phantomJS。除了phantomJS瀏覽器,webdriver還整合了Chrome、Firefox、IE等瀏覽器,并提供了操作這些瀏覽器的接口。

由于phantomJS是無界面瀏覽器,不需要界面的同時占用的內存也相對較小,更適用于大規模多進程爬數據(試想,如果開幾十個Chrome進程爬數據,那真是內存噩夢!)。本文主要討論使用selenium phantomJS過程中遇到的bug,而不是selenium phantomJS使用教程,有需要了解selenium基本用法的同學,請移步官方文檔

個人用phantomJS爬數據有一段時間了,爬蟲程序也大致完工了,過程中遇到了很多坑,統一總結如下。

1. 查看phantomJS文檔

前面提到,phantomJS是selenium子包webdriver下面多個瀏覽器中的一個,而selenium包對不同的瀏覽器都提供了統一的接口,所以直接查看selenium的官方文檔即可,也有對應的中文文檔。文檔內容不多,但很全面。遇到不懂的問題,先看文檔肯定沒錯。

這里需要注意的是,百度搜索phantomJS得到的結果只是phantomJS的官方文檔,而phantomJS是一個獨立的無界面瀏覽器,也稱JS模擬器,本來就獨立于python。我們需要的是phantomJS的python接口,也就是通過python調用phantomJS,所以只需查看selenium的webdriver文檔。

當然,官方文檔很全面,但也相對繁雜。python有個查看文檔的小技巧,直接使用help()就能查看某個對象的幫助文檔。比如help(driver)即可直接查看driver這個對象的文檔,包括其內部函數、變量的說明。如果driver是一個phantomJS對象,那么會顯示phantomJS瀏覽器對象的函數和變量的文檔,具體內容和官方文檔一樣。對所有的python對象都可以這樣干,非常便捷。似乎現在各種IDE也有這個功能:當鼠標懸停在某個對象上,就顯示該對象的幫助文檔。不過多掌握個方法總歸沒錯。

2. phantomJS的配置問題

selenium官方文檔中,phantomJS對象的幫助文檔很詳細,但當涉及phantomJS瀏覽器的配置,比如user-agent偽裝、代理、超時返回等選項時,有用的信息就非常少了。結合網上的資料和自己遇到的各種坑,我總結了常用的phantomJS配置選項,對普通的爬蟲來說,應該夠用了。

from selenium import webdriver
# 引入配置對象DesiredCapabilities
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
dcap = dict(DesiredCapabilities.PHANTOMJS)
#從USER_AGENTS列表中隨機選一個瀏覽器頭,偽裝瀏覽器
dcap["phantomjs.page.settings.userAgent"] = (random.choice(USER_AGENTS))
# 不載入圖片,爬頁面速度會快很多
dcap["phantomjs.page.settings.loadImages"] = False
# 設置代理
service_args = ['--proxy=127.0.0.1:9999','--proxy-type=socks5']
#打開帶配置信息的phantomJS瀏覽器
driver = webdriver.PhantomJS(phantomjs_driver_path, desired_capabilities=dcap,service_args=service_args)                
# 隱式等待5秒,可以自己調節
driver.implicitly_wait(5)
# 設置10秒頁面超時返回,類似于requests.get()的timeout選項,driver.get()沒有timeout選項
# 以前遇到過driver.get(url)一直不返回,但也不報錯的問題,這時程序會卡住,設置超時選項能解決這個問題。
driver.set_page_load_timeout(10)
# 設置10秒腳本超時時間
driver.set_script_timeout(10)

3. phantomJS的并發問題

phantomJS爬數據比較慢,并發編程幾乎是必選項。最初,我考慮采用多線程/協程的方式,畢竟對于這種IO密集型的程序,多線程/協程比較合適。但多次測試下來,程序卻遇到各種問題,有時能成功運行,有時卻不能。嘗試將phantomJS改成Chrome,程序居然能正常運行,這基本確定是phantomJS的鍋了。所以,如果需要并發編程提高效率,用Chrome比較好,雖然內存占用相對較多,況且經下面簡友提醒,在沒界面的主機上也可以跑Chrome,那自然更好了。

在網上仔細查找了相關資料(這玩意的中文資料極少,只能去國外技術論壇潛水),原來phantomJS本身在多線程方面還有很多bug,建議使用多進程,具體什么原因有時間再去了解。

關于多進程,推薦使用multiprocessing庫,簡潔、高效!下面幾行代碼便實現了多進程并發。

from multiprocessing import Pool
pool = Pool(8)
data_list = pool.map(get, url_list)
pool.close()
pool.join()

4. phantomJS進程不自動退出問題

話說,一開始我寫好程序后,先在本地測試了一段時間,確認程序各方面都沒問題后,直接扔阿里云主機上跑了。過了一段時間,查了下程序運行日志,很好,一切如常。于是我就高高興興地摸魚去了。

第二天準備登錄主機驗收程序時,卻發現居然無法登錄!啥,無法登錄?難不成這個小爬蟲程序還能把主機搞崩?我先在阿里云后臺查看了主機的運行日志,發現主機的內存使用越來越高,應該是內存耗盡后,強制關機了。似乎是程序沒有回收內存,導致占用的內存越來越大。明確大致原因后,就是痛苦的查bug過程了。

由于bug涉及內存的使用,我自然地想到了用top命令查看進程的內存使用情況。先運行程序,然后運行top命令,實時檢測程序的內存使用情況。一開始程序占用內存在正常范圍,只有一個phantomJS進程在運行,似乎沒有什么不對。但隨著時間的增長,內存中居然同時有好幾個phantomJS進程在運行,內存所剩空間越來越小!但根據程序的邏輯,任何時候都只有一個phantomJS進程在爬數據。我意識到可能是由于phantomJS進程沒有正常關閉,所以在內存中駐留的phantomJS進程越來越多,最終吃光了內存。

帶著這個問題,我重新檢查了一次代碼,尤其在程序異常退出的地方。最終找到了類似下面的代碼:

try:
    self.driver.get(url)
    self.wait_()
    return True
except Exception as e:
    return False

程序的邏輯是:如果在打開url的過程中報錯,那么就返回False,反之返回True。

似乎直接return False的處理太粗心了。我嘗試著在return False前加上一行self.driver.quit()。再次運行程序,并用top查看內存使用情況,發現程序的內存使用一直都在正常范圍內,并沒有出現多個phantomJS進程的情況,問題搞定!后面在網上找到的資料也證實了我的猜想:主程序退出后,selenium不保證phantomJS也成功退出,最好手動關閉phantomJS進程。

5. 其他問題

5.1 不同frame間的轉換

有時,phantomJS獲得的頁面源碼的確存在某元素,但通過find_element_by_xpath()等定位函數卻無法獲得該元素對象,總是提示“元素不存在”的錯誤。遇到這種情況,除了檢查元素節點路徑是否正確外,還應該分析頁面源碼,檢查元素是否被包裹在一個特定的frame中,如果是后者,那么在使用查找函數前,需要額外的處理。

比如網頁源碼中有如下代碼:

<iframe id="topmenuFrame" width="100%" scrolling="no" height="100%" src="topmenu.aspx?>
<div id="haha">text</div>
</iframe>

假如你想要獲取id="haha"的div標簽,直接通過driver.find_element_by_id('haha')就會提示“元素不存在“的錯誤。

這時需要使用driver.switch_to_frame(driver.find_element_by_id``("topmenuFrame")),即先進入id為topmenuFrame的frame,然后再執行driver.find_element_by_id("haha"),就能正確獲得該元素了。

需要注意的是,切換到這個frame之后,只能訪問當前frame的內容,如果想要回到默認的內容范圍,相當于默認的frame,還需要使用driver.switch_to_default_content()

頁面中有多個frame時,要注意frame之間的切換。

5.2 implicit_wait、WebDriverWait不一定靠譜

宿舍哥們用phantomJS爬數據時,遇到了一個匪夷所思的bug。起初,他寫了個很簡單的程序,從個方面來看都沒問題,但實際運行卻提示各種錯誤,讓人十分費解。折騰大半天之后,他直接注釋掉自己不太熟悉的implicit_wait(),改用time.sleep()作延時,程序居然就能正確運行了!原來implicit_wait()有bug。同樣的,對于WebDriverWait,大家使用時也要特別注意。

看來python的selenium庫不是很成熟,還存在一些問題,一些函數的實際運行情況并不是預期的那樣,在查bug時,要留意這些問題。

6. 總結

總的來說,selenium庫簡單,容易上手,是爬動態網頁的殺手級武器,但對phantomJS瀏覽器的支持還不是特別完善。當然,了解存在的問題,并找到對應的解決方法,就能發揮phantomJS的威力了。

以上就是我個人這段時間的phantomJS使用小結,雖然不是很全面,也不確保完全準確,算是對我這段學習歷程的總結吧,希望對大家有用。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容

  • 轉自Selenium WebDriver注意:本章內容官方團隊正在完善中。 介紹 WebDriver### Sel...
    抓兔子的貓閱讀 7,765評論 2 22
  • 事情緣由還得從那天下午的課說起。當時大家都在認真聽課。突然,旁邊一哥們說他搶到了“高級數理邏輯”了,what???...
    Rabin_xie閱讀 6,836評論 8 20
  • 從三月份找實習到現在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發崗...
    時芥藍閱讀 42,320評論 11 349
  • 和H走在金滿地時 突然談到畢業后住哪的問題 我說我現在的室友估計不會和我住了啊 她爹媽計劃著等她畢業就給她買房呢 ...
    郝氏春秋閱讀 442評論 0 0
  • 或許我還是不夠自己期望中的努力,我也沒有成為自己想成為的那種天才,但是這一年來,累并快樂著,我知道自己的努力,然后...
    公子F閱讀 351評論 0 2