李 陽,范伊紅,李彥蓉,王朝陽
(河南科技大學(xué) 軟件學(xué)院,河南 洛陽 471000)
當(dāng)今時(shí)代是一個(gè)信息浪潮涌起的時(shí)代,不管是工程領(lǐng)域還是學(xué)術(shù)研究領(lǐng)域,大家身邊處處離不開數(shù)據(jù)。而對(duì)于數(shù)據(jù)的采集,很大程度上離不開網(wǎng)絡(luò)爬蟲,這也是爬蟲技術(shù)日益火爆的原因之一。當(dāng)使用Python的requests庫來進(jìn)行網(wǎng)站的爬取時(shí),必須等待網(wǎng)站返回響應(yīng)之后才可以接著運(yùn)行,在此期間,程序一直處于等待狀態(tài)。如果每次請(qǐng)求網(wǎng)站時(shí),網(wǎng)站返回響應(yīng)都有一定的延遲,這樣勢(shì)必會(huì)影響爬取效率。那么有沒有一種方法可以減少這種網(wǎng)絡(luò)延遲的影響,并且進(jìn)一步提高爬取效率呢?異步爬蟲無疑是首選。
爬蟲是I/O密集型任務(wù),當(dāng)使用異步執(zhí)行的方式來操作程序代碼時(shí),可以對(duì)爬蟲效率進(jìn)行十倍甚至近百倍的提升。比如說發(fā)出了一個(gè)請(qǐng)求,目標(biāo)站點(diǎn)在返回響應(yīng)時(shí)有延遲,此時(shí)程序處于等待狀態(tài),在等待的這段時(shí)間中,可以讓程序轉(zhuǎn)去處理其他事情,等響應(yīng)返回之后再繼續(xù)向下執(zhí)行,這樣就可極大提高程序的效率[1]。
圖1 抓包分析
當(dāng)然,在學(xué)習(xí)異步爬蟲之前需要一些基礎(chǔ)知識(shí)的儲(chǔ)備,例如:協(xié)程的概念、同步和異步的概念、阻塞和非阻塞的概念以及協(xié)程的相關(guān)用法。本文將通過實(shí)例進(jìn)行演示。
本文以某圖片網(wǎng)站作為案例,體驗(yàn)一下異步爬蟲的高效爬取。
首先在目標(biāo)站點(diǎn)搜索框上任意輸入一個(gè)關(guān)鍵字,點(diǎn)擊搜索到達(dá)圖片列表頁界面,然后對(duì)頁面進(jìn)行抓包分析。通過分析可以發(fā)現(xiàn),列表頁中響應(yīng)的代碼中已經(jīng)包含了該頁中所有圖片的鏈接信息,所以只需要遍歷所有的列表頁并提取出其中的圖片鏈接信息,即可大功告成[2]。
首先使用logging對(duì)日志輸出進(jìn)行配置,這樣便于定位程序執(zhí)行的位置。定義爬取頁數(shù)PAGE_NUMBER(列表頁的數(shù)量),最大并發(fā)數(shù)CONCURRENCY_NUMBER(每次同時(shí)發(fā)出多少個(gè)請(qǐng)求,此處定義不易過大,避免目標(biāo)網(wǎng)站崩潰),目標(biāo)站點(diǎn)的基本網(wǎng)址信息以及請(qǐng)求頭信息。
定義scrape_index(page)方法,通過接收page參數(shù)來構(gòu)造每一個(gè)列表頁的請(qǐng)求鏈接,并且調(diào)用get_index(url)方法
來進(jìn)行列表頁的爬取。注意請(qǐng)求后返回的結(jié)果必須是一個(gè)coroutine類型對(duì)象,否則會(huì)拋出異常[3]。
爬取列表頁之后編寫parse_index(html)方法,對(duì)列表頁的結(jié)果進(jìn)行解析并提取出其中的圖片鏈接信息,提取的方法可以使用pyquery,beautifulsoup或者正則表達(dá)式等,本文此處使用的是pyquery。定義scrape_image(url)方法,接收?qǐng)D片鏈接信息作為參數(shù),然后對(duì)圖片發(fā)出請(qǐng)求;得到響應(yīng)后,定義write_to_file(title,data)方法,將圖片信息依次寫入文件,即可將圖片存入本地計(jì)算機(jī)。
首先創(chuàng)建一個(gè)ClientSession對(duì)象,用該對(duì)象來發(fā)出異步請(qǐng)求。主要思路是要維護(hù)一個(gè)動(dòng)態(tài)變化的爬取隊(duì)列,每個(gè)新產(chǎn)生的task會(huì)被放在隊(duì)列中。這里將爬取邏輯分為兩部分:第一部分為所有列表頁的異步爬取,第二部分為圖片信息的異步爬取。使用列表表達(dá)式將所有的列表頁爬取任務(wù)集中在一塊,聲明為一個(gè)task列表。由于程序中設(shè)置的并發(fā)數(shù)量為5,所以每次會(huì)同時(shí)發(fā)出5個(gè)請(qǐng)求。當(dāng)發(fā)出的請(qǐng)求沒有立即得到響應(yīng)時(shí),程序會(huì)繼續(xù)對(duì)其他的列表頁發(fā)起請(qǐng)求,直到程序得到響應(yīng)才會(huì)繼續(xù)往下執(zhí)行。接著使用asyncio的gather方法獲取task的請(qǐng)求結(jié)果,并將列表頁的請(qǐng)求結(jié)果通過parse_index(html)方法進(jìn)行解析,將所有解析出來的圖片鏈接信息都存放到事先定義好的image_url列表中。此時(shí)第一部分的列表頁的爬取已經(jīng)完成。與第一部分相同,繼續(xù)將所有的圖片爬取任務(wù)聲明為另一個(gè)task列表,然后發(fā)出異步請(qǐng)求,使用gather方法獲取請(qǐng)求結(jié)果,并將圖片信息寫入文件[4]。部分代碼如下:
在程序入口處使用asyncio的get_event_loop()方法獲取一個(gè)事件循環(huán)對(duì)象,并將main方法注冊(cè)到事件循環(huán)中,至此異步爬蟲編寫完成,代碼如下。
本文以一個(gè)圖片網(wǎng)站為例,講解了基于aiohttp的異步爬蟲的程序?qū)崿F(xiàn),并且從技術(shù)和思路上提供了一些方法。雖然多進(jìn)程或者多線程技術(shù)也可以提高爬蟲的效率,但是異步爬蟲可以節(jié)省程序調(diào)度時(shí)的開銷,所以異步爬蟲依舊是比較有優(yōu)勢(shì)的。今后在異步的基礎(chǔ)上如何進(jìn)一步提高程序的運(yùn)行效率,依舊是一個(gè)極具價(jià)值和挑戰(zhàn)性的研究方向。