李 陽,范伊紅,李彥蓉,王朝陽
(河南科技大學 軟件學院,河南 洛陽 471000)
當今時代是一個信息浪潮涌起的時代,不管是工程領域還是學術研究領域,大家身邊處處離不開數據。而對于數據的采集,很大程度上離不開網絡爬蟲,這也是爬蟲技術日益火爆的原因之一。當使用Python的requests庫來進行網站的爬取時,必須等待網站返回響應之后才可以接著運行,在此期間,程序一直處于等待狀態。如果每次請求網站時,網站返回響應都有一定的延遲,這樣勢必會影響爬取效率。那么有沒有一種方法可以減少這種網絡延遲的影響,并且進一步提高爬取效率呢?異步爬蟲無疑是首選。
爬蟲是I/O密集型任務,當使用異步執行的方式來操作程序代碼時,可以對爬蟲效率進行十倍甚至近百倍的提升。比如說發出了一個請求,目標站點在返回響應時有延遲,此時程序處于等待狀態,在等待的這段時間中,可以讓程序轉去處理其他事情,等響應返回之后再繼續向下執行,這樣就可極大提高程序的效率[1]。

圖1 抓包分析
當然,在學習異步爬蟲之前需要一些基礎知識的儲備,例如:協程的概念、同步和異步的概念、阻塞和非阻塞的概念以及協程的相關用法。本文將通過實例進行演示。
本文以某圖片網站作為案例,體驗一下異步爬蟲的高效爬取。
首先在目標站點搜索框上任意輸入一個關鍵字,點擊搜索到達圖片列表頁界面,然后對頁面進行抓包分析。通過分析可以發現,列表頁中響應的代碼中已經包含了該頁中所有圖片的鏈接信息,所以只需要遍歷所有的列表頁并提取出其中的圖片鏈接信息,即可大功告成[2]。
首先使用logging對日志輸出進行配置,這樣便于定位程序執行的位置。定義爬取頁數PAGE_NUMBER(列表頁的數量),最大并發數CONCURRENCY_NUMBER(每次同時發出多少個請求,此處定義不易過大,避免目標網站崩潰),目標站點的基本網址信息以及請求頭信息。
定義scrape_index(page)方法,通過接收page參數來構造每一個列表頁的請求鏈接,并且調用get_index(url)方法
來進行列表頁的爬取。注意請求后返回的結果必須是一個coroutine類型對象,否則會拋出異常[3]。
爬取列表頁之后編寫parse_index(html)方法,對列表頁的結果進行解析并提取出其中的圖片鏈接信息,提取的方法可以使用pyquery,beautifulsoup或者正則表達式等,本文此處使用的是pyquery。定義scrape_image(url)方法,接收圖片鏈接信息作為參數,然后對圖片發出請求;得到響應后,定義write_to_file(title,data)方法,將圖片信息依次寫入文件,即可將圖片存入本地計算機。
首先創建一個ClientSession對象,用該對象來發出異步請求。主要思路是要維護一個動態變化的爬取隊列,每個新產生的task會被放在隊列中。這里將爬取邏輯分為兩部分:第一部分為所有列表頁的異步爬取,第二部分為圖片信息的異步爬取。使用列表表達式將所有的列表頁爬取任務集中在一塊,聲明為一個task列表。由于程序中設置的并發數量為5,所以每次會同時發出5個請求。當發出的請求沒有立即得到響應時,程序會繼續對其他的列表頁發起請求,直到程序得到響應才會繼續往下執行。接著使用asyncio的gather方法獲取task的請求結果,并將列表頁的請求結果通過parse_index(html)方法進行解析,將所有解析出來的圖片鏈接信息都存放到事先定義好的image_url列表中。此時第一部分的列表頁的爬取已經完成。與第一部分相同,繼續將所有的圖片爬取任務聲明為另一個task列表,然后發出異步請求,使用gather方法獲取請求結果,并將圖片信息寫入文件[4]。部分代碼如下:


在程序入口處使用asyncio的get_event_loop()方法獲取一個事件循環對象,并將main方法注冊到事件循環中,至此異步爬蟲編寫完成,代碼如下。

本文以一個圖片網站為例,講解了基于aiohttp的異步爬蟲的程序實現,并且從技術和思路上提供了一些方法。雖然多進程或者多線程技術也可以提高爬蟲的效率,但是異步爬蟲可以節省程序調度時的開銷,所以異步爬蟲依舊是比較有優勢的。今后在異步的基礎上如何進一步提高程序的運行效率,依舊是一個極具價值和挑戰性的研究方向。