Python分布式動(dòng)態(tài)頁(yè)面爬蟲(chóng)研究

Selenium的Webdriver爬取動(dòng)態(tài)網(wǎng)頁(yè)效果雖然不錯(cuò)俩滥,但效率方面并不如人意躬它。最近一直研究如何提高動(dòng)態(tài)頁(yè)面爬蟲(chóng)的效率巍实,方法無(wú)非高并發(fā)和分布式兩種滓技。過(guò)程中有很多收獲,也踩了不少坑棚潦,在此一并做個(gè)總結(jié)令漂。以下大致是這段時(shí)間的學(xué)習(xí)路線。

一丸边、 Scrapy+phantomJS

Scrapy是一個(gè)高效的異步爬蟲(chóng)框架叠必,使用比較廣泛,文檔也很完備妹窖,開(kāi)發(fā)人員能快速地實(shí)現(xiàn)高性能爬蟲(chóng)纬朝。關(guān)于Scrapy的基本使用這里就不再贅述了, 這篇Scrapy讀書筆記挺不錯(cuò)的骄呼。然而Scrapy在默認(rèn)的情況下只能獲取靜態(tài)的網(wǎng)頁(yè)內(nèi)容共苛,因此必須進(jìn)一步定制開(kāi)發(fā)。

Scrapy結(jié)合phantomJS似乎是個(gè)不錯(cuò)的選擇谒麦。phantomJS是一個(gè)沒(méi)有頁(yè)面的瀏覽器俄讹,能渲染動(dòng)態(tài)頁(yè)面并且相對(duì)輕量。因此绕德,我們需要修改Scrapy的網(wǎng)頁(yè)請(qǐng)求模塊患膛,讓phantomJS請(qǐng)求網(wǎng)頁(yè),以達(dá)到獲取動(dòng)態(tài)網(wǎng)頁(yè)的目的耻蛇。一番調(diào)研之后踪蹬,發(fā)現(xiàn)大致有三種定制方法:

1. 每個(gè)url請(qǐng)求兩次。在回調(diào)函數(shù)中舍棄掉返回的response內(nèi)容臣咖,然后用phantomJS再次請(qǐng)求response.url跃捣,這次的請(qǐng)求由于沒(méi)有構(gòu)造Request對(duì)象,當(dāng)然就沒(méi)有回調(diào)函數(shù)了夺蛇,然后阻塞等待結(jié)果返回即可疚漆。這個(gè)方法會(huì)對(duì)同一個(gè)url請(qǐng)求兩次,第一次是Scrapy默認(rèn)的HTTP請(qǐng)求,第二次則是phantomJS的請(qǐng)求娶聘,當(dāng)然第二次獲取到的就是動(dòng)態(tài)網(wǎng)頁(yè)了闻镶。這個(gè)方法比較適合快速實(shí)現(xiàn)小規(guī)模動(dòng)態(tài)爬蟲(chóng),在默認(rèn)的Scrapy項(xiàng)目基礎(chǔ)上丸升,只需要簡(jiǎn)單修改回調(diào)函數(shù)就可以了铆农。

2. 自定義下載中間件(downloadMiddleware)。downloadMiddleware對(duì)從scheduler送來(lái)的Request對(duì)象在請(qǐng)求之前進(jìn)行預(yù)處理狡耻,可以實(shí)現(xiàn)添加headers墩剖,user_agent,還有cookie等功能 夷狰。但也可以通過(guò)中間件直接返回HtmlResponse對(duì)象岭皂,略過(guò)請(qǐng)求的模塊,直接扔給response的回調(diào)函數(shù)處理孵淘。代碼如下:

    class CustomMetaMiddleware(object):
        def process_request(self,request,spider):
            dcap = dict(DesiredCapabilities.PHANTOMJS)
            dcap["phantomjs.page.settings.loadImages"] = False
            dcap["phantomjs.page.settings.resourceTimeout"] = 10 
            driver = webdriver.PhantomJS("E:xx\xx\xx",desired_capabilities=dcap)
            driver.get(request.url)
            body = driver.page_source.encode('utf8')
            url = driver.current_url
            driver.quit()
            return HtmlResponse(request.url,body=body)

改完代碼后蒲障,記得修改settings配置。但這個(gè)方法有個(gè)很大的問(wèn)題——不能實(shí)現(xiàn)異步爬取瘫证。由于直接在下載中間件中請(qǐng)求網(wǎng)頁(yè)揉阎,而Scrapy在這里卻不是異步的,只能實(shí)現(xiàn)阻塞式的逐個(gè)網(wǎng)頁(yè)下載背捌。當(dāng)然毙籽,如果不追求高并發(fā)的話,這也是個(gè)快速部署動(dòng)態(tài)爬蟲(chóng)的方法毡庆。

3.自定義downloader坑赡。downloader是Scrapy發(fā)起HTTP請(qǐng)求的模塊,這模塊實(shí)現(xiàn)了異步請(qǐng)求么抗,因此自定義downloader是最完美的實(shí)現(xiàn)毅否。但是要編寫一個(gè)自定義的downloader比較麻煩,必須按照Twisted的一些規(guī)范蝇刀,所幸網(wǎng)上有一些開(kāi)源的downloader螟加,在這基礎(chǔ)上改改就比較容易了。 這篇文章詳解了downloader的開(kāi)發(fā)吞琐,非常不錯(cuò)捆探!

一些坑和心得

  1. 通過(guò)代碼運(yùn)行Scrapy是個(gè)很有用的方法,即通過(guò)CrawlerProcess類運(yùn)行爬蟲(chóng)站粟,但是給Spider傳遞settings參數(shù)卻是一個(gè)很大的坑黍图,這個(gè)問(wèn)題繞了我很長(zhǎng)時(shí)間,最后的解決方法是修改PYTHONPATHSCRAPY_SETTINGS_MODULE環(huán)境變量奴烙,加上爬蟲(chóng)項(xiàng)目的目錄助被,這樣Python才能找到配置文件剖张。

  2. 設(shè)置DOWNLOAD_TIMEOUT選項(xiàng),其默認(rèn)值是180秒恰起,相對(duì)較長(zhǎng)修械,可以設(shè)置得短一些提高效率。

  3. PhantomJS對(duì)多進(jìn)程的支持極不穩(wěn)定检盼。具體表現(xiàn)在如果一主機(jī)同時(shí)開(kāi)了多個(gè)phantomJS進(jìn)程,單個(gè)phantomJS運(yùn)行結(jié)果就會(huì)時(shí)好時(shí)壞翘单,經(jīng)常出現(xiàn)一些莫名其妙的報(bào)錯(cuò)吨枉,官方git的issue上也提到phantomJS對(duì)多進(jìn)程的支持很不好。如果真要多進(jìn)程爬蟲(chóng)的話哄芜,推薦chromedriver貌亭。

  4. Scrapy的優(yōu)勢(shì)在于高效的異步請(qǐng)求框架,由于其本身并不支持動(dòng)態(tài)頁(yè)面爬取认臊,如果對(duì)爬蟲(chóng)的效率沒(méi)有特別高的要求圃庭,也沒(méi)有必要一定用這個(gè)框架,畢竟熟悉框架要一定的時(shí)間成本失晴,在框架下編程限制也比較多剧腻,對(duì)一些比較簡(jiǎn)單的爬蟲(chóng),有時(shí)還不如自己手?jǐn)]一個(gè)涂屁。

二书在、 Scrapy-splash

由于phantomJS的多并發(fā)短板,Scrapy+phantomJS的效率受限拆又,因此儒旬,這并不是一個(gè)特別好的選擇。

又一番調(diào)研后帖族,發(fā)現(xiàn)splash似乎是個(gè)不錯(cuò)的選擇栈源。Splash是一個(gè)Javascript渲染服務(wù)。它是用Python實(shí)現(xiàn)的竖般,同時(shí)使用了Twisted和QT甚垦,并且實(shí)現(xiàn)了HTTP API的輕量瀏覽器,Twisted(QT)用來(lái)讓服務(wù)具有異步處理能力捻激,以發(fā)揮webkit的并發(fā)能力制轰。

在Scrapy中使用splash也很簡(jiǎn)單,詳見(jiàn)http://www.cnblogs.com/zhonghuasong/p/5976003.html 胞谭。

一般來(lái)說(shuō)垃杖,在Scrapy中只需要返回一個(gè)SplashRequest對(duì)象即可。比如:

yield SplashRequest(url='http://'+url,callback=self.parse,endpoint='render.html',
                  args={'wait':2},errback=self.errback_fun, meta={ })

同樣也可以返回帶POST參數(shù)的Request對(duì)象丈屹。更簡(jiǎn)單地调俘,用urllib等庫(kù)構(gòu)造POST請(qǐng)求也沒(méi)問(wèn)題伶棒,因?yàn)檫@本質(zhì)上是一個(gè)端口代理,可以接受任何的HTTP請(qǐng)求彩库。

splash的內(nèi)存占用相對(duì)較少肤无,但多并發(fā)仍然會(huì)出現(xiàn)些問(wèn)題,請(qǐng)求的失敗率會(huì)大大提高骇钦,頁(yè)面渲染結(jié)果偶爾會(huì)出現(xiàn)一些問(wèn)題宛渐,同時(shí)受制于服務(wù)器主機(jī)的帶寬,速度受限眯搭,但總體表現(xiàn)不錯(cuò)窥翩,足以應(yīng)對(duì)小規(guī)模的動(dòng)態(tài)爬蟲(chóng)。

Splash的優(yōu)點(diǎn)也很顯著鳞仙,通過(guò)HTTP API寇蚊,其他分布式節(jié)點(diǎn)能很容易地獲得動(dòng)態(tài)頁(yè)面,并且使得服務(wù)器和其他節(jié)點(diǎn)之間的耦合降到了最低棍好,擴(kuò)展變得特別方便仗岸。另外,分布式節(jié)點(diǎn)不用配置環(huán)境就能獲得動(dòng)態(tài)頁(yè)面借笙,相對(duì)phantomJS復(fù)雜的配置來(lái)說(shuō)簡(jiǎn)單太多了扒怖!如果想簡(jiǎn)單地實(shí)現(xiàn)動(dòng)態(tài)頁(yè)面爬蟲(chóng),splash是一個(gè)非常好的選擇提澎,但受制于單個(gè)服務(wù)器帶寬姚垃,速度有限,并且有時(shí)渲染效果不是很理想盼忌。

三积糯、 chromedriver并發(fā)

無(wú)論phantomJS還是splash,穩(wěn)定性是一方面谦纱,在渲染效果和速度上都不及chromedriver看成,畢竟V8引擎不是蓋的!但chromedriver缺點(diǎn)也很明顯——特別耗內(nèi)存跨嘉,而且是有界面的川慌!

有段時(shí)間為了爬百度搜索結(jié)果,我一開(kāi)始用requests庫(kù)模擬POST請(qǐng)求祠乃,雖然效率沒(méi)問(wèn)題梦重,但經(jīng)常被百度封,于是試著改用phantomJS亮瓷,當(dāng)時(shí)覺(jué)得盡管效率低了點(diǎn)琴拧,但畢竟是真正的瀏覽器,百度應(yīng)該不會(huì)封嘱支。后來(lái)發(fā)現(xiàn)作用也不大蚓胸,還是經(jīng)常被封挣饥,并且phantomJS自身不太穩(wěn)定,經(jīng)常報(bào)錯(cuò)沛膳,多進(jìn)程并發(fā)更是沒(méi)辦法運(yùn)行扔枫。看來(lái)只能試一試chromedriver了锹安。以前一直忌憚?dòng)趦?nèi)存殺手chrome(開(kāi)一個(gè)chrome瀏覽器短荐,任務(wù)管理器里就有很多個(gè)chrome進(jìn)程),最后無(wú)奈只能祭出這大殺器了八毯。跑了一段時(shí)間之后搓侄,發(fā)現(xiàn)chrome的效率還挺不錯(cuò),占用的內(nèi)存也沒(méi)有想象中的大话速,多并發(fā)支持非常好,在我的電腦上同時(shí)開(kāi)20來(lái)個(gè)也沒(méi)問(wèn)題芯侥,穩(wěn)定性也不錯(cuò)泊交,而且百度居然就沒(méi)封!(震驚V椤@蟆!chrome居然自帶反反爬蟲(chóng)光環(huán)0ぁ)研乒。但由于程序主要在阿里云主機(jī)上跑,有界面的chromedriver當(dāng)時(shí)便沒(méi)有考慮在內(nèi)淋硝,前不久才知道原來(lái)可以通過(guò)引入虛擬界面雹熬,讓chrome在沒(méi)有界面的主機(jī)上跑.....

Python的pyvirtualdisplay庫(kù)就能引入虛擬界面。
代碼實(shí)現(xiàn)也非常簡(jiǎn)單:

from pyvirtualdisplay import Display
display = Display(visible=0,size=(800,600))
display.start()
driver = webdriver.Chrome()
driver.get('http://www.baidu.com')

經(jīng)個(gè)人測(cè)試谣膳,發(fā)現(xiàn)chrome對(duì)多進(jìn)程的支持非常好竿报,渲染速度快,就是內(nèi)存占用相對(duì)較大继谚,可以多進(jìn)程+分布式提高效率烈菌,關(guān)鍵chrome不容易被封。

PS. 常用的chromedriver關(guān)閉圖片選項(xiàng)代碼:

chromeOptions = webdriver.ChromeOptions()
prefs = {"profile.managed_default_content_settings.images":2}
chromeOptions.add_experimental_option("prefs",prefs)
driver = webdriver.Chrome(chromedriver_path,chrome_options=chromeOptions)
driver.get(url)

四花履、Selenium Grid

Selenium Grid是Selenium的單機(jī)擴(kuò)展芽世,允許用戶將測(cè)試案例分布在幾臺(tái)機(jī)器上并行執(zhí)行。當(dāng)然诡壁,能實(shí)現(xiàn)分布式測(cè)試济瓢,分布式爬蟲(chóng)當(dāng)然沒(méi)問(wèn)題。
Selenium Grid的機(jī)制如圖欢峰。首先啟動(dòng)一個(gè)中央節(jié)點(diǎn)(Hub)葬荷,然后啟動(dòng)多個(gè)遠(yuǎn)程控制節(jié)點(diǎn)(rc)涨共,并讓rc在Hub上注冊(cè)自己的信息,包括rc自身的系統(tǒng)宠漩、支持的webdriver举反、最大并發(fā)數(shù)量等,這樣Hub節(jié)點(diǎn)就知道了所有的rc信息扒吁,方便以后調(diào)度火鼻。

Selenium Grid機(jī)制

運(yùn)行環(huán)境搭建好之后,測(cè)試或爬蟲(chóng)腳本請(qǐng)求Hub的服務(wù)端口雕崩,Hub主機(jī)根據(jù)注冊(cè)的rc節(jié)點(diǎn)的當(dāng)前狀態(tài)魁索,結(jié)合負(fù)載均衡原則,將這些測(cè)試用例分發(fā)到指定的rc節(jié)點(diǎn)盼铁,rc節(jié)點(diǎn)接到命令之后便執(zhí)行粗蔚。

from selenium import webdriver
    url = "http://localhost:4444/wd/hub"
    driver = webdriver.Remote(command_executor = url, desired_capabilities = {'browserName':'chrome'})
    driver.get("http://www.baidu.com")
    print driver.title

如下圖,我在本地建立了一個(gè)Hub節(jié)點(diǎn)饶火,默認(rèn)端口是4444鹏控,接著用本機(jī)注冊(cè)了兩個(gè)rc節(jié)點(diǎn),端口分別為5555肤寝、6666当辐。通過(guò)hub服務(wù)端口的控制臺(tái)可以看到,每個(gè)節(jié)點(diǎn)可以支持5個(gè)Firefox實(shí)例鲤看、一個(gè)IE實(shí)例和5個(gè)Chrome實(shí)例(可以自定義)缘揪。由于本機(jī)沒(méi)有安裝Opera瀏覽器,當(dāng)然也就沒(méi)有Opera實(shí)例了义桂。

Selenium Grid控制臺(tái)頁(yè)面

Selenium Grid是個(gè)很好的實(shí)現(xiàn)分布式測(cè)試/動(dòng)態(tài)爬蟲(chóng)的框架找筝,原理和操作也不復(fù)雜,有興趣的同學(xué)可以多了解了解澡刹。

五呻征、 總結(jié)

以上各軟件或框架的特點(diǎn)簡(jiǎn)要如下:

  1. phantomJS比較輕量,但對(duì)多并發(fā)支持非常差
  2. chromedriver渲染速度快罢浇,多并發(fā)支持較好陆赋,但占用內(nèi)存大
  3. splash實(shí)現(xiàn)了HTTP API,分布式擴(kuò)展容易嚷闭,頁(yè)面渲染能力一般
  4. Selenium Grid是專業(yè)的測(cè)試框架攒岛,擴(kuò)展容易,支持負(fù)載均衡等高級(jí)特性

所以胞锰,分布式Scrapy+chromedriverSelenium Grid是實(shí)現(xiàn)分布式動(dòng)態(tài)爬蟲(chóng)較好的選擇灾锯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嗅榕,隨后出現(xiàn)的幾起案子顺饮,更是在濱河造成了極大的恐慌吵聪,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兼雄,死亡現(xiàn)場(chǎng)離奇詭異吟逝,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)赦肋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門块攒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人佃乘,你說(shuō)我怎么就攤上這事囱井。” “怎么了趣避?”我有些...
    開(kāi)封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵庞呕,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我程帕,道長(zhǎng)千扶,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任骆捧,我火速辦了婚禮,結(jié)果婚禮上髓绽,老公的妹妹穿的比我還像新娘敛苇。我一直安慰自己,他們只是感情好顺呕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布枫攀。 她就那樣靜靜地躺著,像睡著了一般株茶。 火紅的嫁衣襯著肌膚如雪来涨。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天启盛,我揣著相機(jī)與錄音蹦掐,去河邊找鬼。 笑死僵闯,一個(gè)胖子當(dāng)著我的面吹牛卧抗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鳖粟,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼社裆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了向图?” 一聲冷哼從身側(cè)響起泳秀,我...
    開(kāi)封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤标沪,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后嗜傅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體金句,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年磺陡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了趴梢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡币他,死狀恐怖坞靶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蝴悉,我是刑警寧澤彰阴,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站拍冠,受9級(jí)特大地震影響尿这,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜庆杜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一射众、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晃财,春花似錦叨橱、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至钢猛,卻和暖如春伙菜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背命迈。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工贩绕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人躺翻。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓丧叽,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親公你。 傳聞我的和親對(duì)象是個(gè)殘疾皇子踊淳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容