用python做網(wǎng)絡(luò )開(kāi)發(fā)的何用人估計都聽(tīng)說(shuō)過(guò)gevent這個(gè)庫,gevent是行代一個(gè)第三方的python協(xié)程庫,其是??碼讓在微線(xiàn)程庫greenlet的基礎上構建而成,并且使用了epoll事件監聽(tīng)(′▽?zhuān)?機制,爬蟲(chóng)這讓gevent具有很好??的提速性能并且比greenlet更好用。根據geve??nt官方的何用資料(網(wǎng)址:http://www.gevent.org),gevent具有以下特點(diǎn):
筆者總結一下,┐(′д`)┌行代gevent大致原理就是碼讓當一個(gè)greenlet遇到需要等待的操作時(shí)(多為IO操作),比如網(wǎng)絡(luò )IO/睡眠等待,爬蟲(chóng)這時(shí)就會(huì )自動(dòng)切換到其他的提速greenlet,等上述操作完成后,何用再在適當的行代時(shí)候(hou)切換回來(lái)繼續執行。在這個(gè)過(guò)程中其實(shí)仍然只有一個(gè)線(xiàn)程在執行,碼讓但因為我們在等待某些IO操作時(shí),爬蟲(chóng)切換到了其他操作,提速避免了無(wú)用的等待,這就為我們大大節省了時(shí)間,提高了效率。
筆者??也是在看了gevent這么多的優(yōu)點(diǎn)之后,感覺(jué)有必要上手試一試,但起初效果非常不理想,速度提升并不(′?`)大,后來(lái)在仔細研(?⊿?)究了gevent的用法之后,發(fā)現gevent的高效率是有條件的,而其中一個(gè)重要條件就是monkey patch的使用,也就是我們常說(shuō)的猴子補丁。
monkey patch就是在不改變源代碼的情況下,對程序進(jìn)行更改和優(yōu)化,其主要適用于動(dòng)態(tài)語(yǔ)言。通過(guò)monkey patch,gevent替換了標準庫里面大部分的阻塞式系統調用,比如s??oヽ(′?`)ノcket、ssl、threading和select等,而變??為協(xié)作式運行。下面筆者還是通過(guò)代碼來(lái)演示一下monkey patch的用法以及使用條件。筆者ヽ(′▽?zhuān)?ノ展示的這個(gè)程序是一個(gè)小型的爬 蟲(chóng)程序,程序代碼量少,便于閱讀和運行,同時(shí)也能較好地測試出monkey patch的提升程度。主要思路是從Box Office Mojo網(wǎng)站抓取北美電影市場(chǎng)今年第二季度上映的電影,然后從每部電影的信息頁(yè)面提??取(′▽?zhuān)?出每部電影的電影分級,然后把每部電影的名稱(chēng)和其對應分級保存在一個(gè)字典當中,再測試一下整個(gè)過(guò)程的時(shí)間。在這里,我們主要測試三種情況下的程序完成時(shí)間,分別是普通不(bu)使用gevent的爬 蟲(chóng),??使用gevent但不用monkey patch的爬 蟲(chóng),以及使用gevent和monkey patch的爬 蟲(chóng)。
首先看普通不使用gevent的爬 蟲(chóng)。
先導入需要的庫。
然后讀取第二季度上映電影的頁(yè)面。
上述代碼中變量url就是第二季度上映電影的網(wǎng)頁(yè)地址,其頁(yè)面截圖如圖┐(′д`)┌1所示??。??headers是爬??蟲(chóng)模擬瀏覽器的頭部信息,每部電影的信息頁(yè)面就是圖1中表格頭一行列名Release(???)下面每部電影名稱(chēng)所包含的網(wǎng)址,點(diǎn)擊每部電影名稱(chēng)??就可進(jìn)入其對應頁(yè)面。因為這個(gè)網(wǎng)址是相對地址,所以要轉換成絕對地址。
圖1.?? 第二季度上映電影的頁(yè)面
接下來(lái)是每部電影的信息頁(yè)面的讀取。
這個(gè)函數就是為了讀取每部電影信息頁(yè)面的信息,其功能和上面讀取url頁(yè)面的功能類(lèi)似,都非常簡(jiǎn)單,ヽ(′?`)ノ沒(méi)(′_`)有過(guò)多可說(shuō)的。在每部電影頁(yè)面中,我??們要讀取的每部電影的分級?信息就在Genres這一行,比如圖2中電影The Wretched,其Genres信息就是Horror。
圖2. 示例電影信息頁(yè)面
接下來(lái)是時(shí)間測算。
我們測算時(shí)間用time.time()方法,用結束時(shí)間減去開(kāi)始時(shí)間就是程序運行時(shí)間,這里我們主要測試spider這個(gè)函數多次運行的時(shí)間。結果顯示,該過(guò)程耗時(shí)59.6188秒。
第(′?`)二個(gè)爬蟲(chóng)是使用gevent但不用monkey patch的爬蟲(chóng)。其完整代碼如下。(′?`)
這里絕大部分代碼和前面爬蟲(chóng)代碼相同,但多了一個(gè)task_list變??量,其是用于存放協(xié)程的列表,我們從gevent_start = time.time()這行開(kāi)始看,因為前面的代碼都和之前的爬蟲(chóng)相同。task = gevent.spawn(spider, u)是生成ヽ(′▽?zhuān)?ノgevent中生成協(xié)程的方法,task_list.append(task)是把每個(gè)協(xié)程放入這個(gè)列表中,而gevent.joinall(task_list)就是運行所有協(xié)程。上面這些過(guò)程和我們運行多線(xiàn)程的方式非常相似。運行結果是59.1744秒。
最后一個(gè)爬蟲(chóng)就是同時(shí)使用gevent和monkey patch的爬蟲(chóng),在這里筆者不再粘貼代碼,因為其代碼和第二個(gè)爬蟲(chóng)幾乎一模一樣,只(zhi)有一個(gè)區別,就是多了一行代碼from gevent import monkey; monkey.patch_all(),注意這是一行代碼,不過(guò)包含兩個(gè)語(yǔ)句,用分號放在了一起。最重要的是,這行代碼要放在所??有代碼的前面,切記?。?!
筆者把這里三個(gè)爬蟲(chóng)分別放在三個(gè)文件中,分別命名為normal_spider.py、gevent_spider_no.py和gevent_spider.py,分別表示普通不用gevent的爬蟲(chóng)、使用gevent但不用monkey patch的爬蟲(chóng)、使用gevent和monkey pat??ch的爬蟲(chóng)。這里有一點(diǎn)要注意,monkey patch暫不支持jupyter notebook,(′?_?`)所以這三個(gè)程序要在命令行中使用,不能在n??otebook中使用。
最后把三種爬蟲(chóng)的結果總結如下。
圖3. 三種爬蟲(chóng)的結果對比
可以看出使用了gevent但不用monkey pa??tch的爬蟲(chóng)和普通爬蟲(chóng)的運行時(shí)間幾乎完全相等,而在用了monkey patch以后,運行時(shí)間只有前面程序的一半不到,速度提升了大約120%,僅僅一行代碼就帶來(lái)如此大的速??度提升,可見(jiàn)monkey patch的作(′▽?zhuān)?用還是很大的。而對于前兩個(gè)爬蟲(chóng)的速度幾乎完全一樣,筆者認為原因在于這兩個(gè)程序都(dou)是單線(xiàn)程運行,本質(zhì)上沒(méi)有太大區別,同時(shí)網(wǎng)頁(yè)讀取數量較?。ㄖ挥?8ヾ(?■_■)ノ個(gè)網(wǎng)頁(yè)),也很難看出gevent的效果。
從本??例中可以看出monkey patch還是有不小提升的,但gevent目前只對常見(jiàn)庫尤其是官方標準庫有patch作用,其他第三??方庫的效果還不得而知,所以對monkey patch的??使用還是要視情(⊙_⊙)況而定。本文的代碼筆者放在gitee代碼網(wǎng)站上,網(wǎng)址是??https://gitee.com/leonmovie/speed-up-gevent-??spider-with-monkey-patch,如有需要可以自行下載。
【責任編輯:龐(??ヮ?)?*:???桂玉 TEL:(010)68476606】