實戰 | 用aiohttp和uvloop實現一個高性能爬蟲

實戰 | 用aiohttp和uvloop實現一個高性能爬蟲

來自專欄 Python私房菜16 人贊了文章

asyncio於Python3.4引入標準庫,增加了對非同步I/O的支持,asyncio基於事件循環,可以輕鬆實現非同步I/O操作。接下來,我們用基於asyncio的庫實現一個高性能爬蟲。

準備工作

Earth View from Google Earth是一款Chrome插件,會在打開新標籤頁時自動載入一張來自Google Earth的背景圖片。

使用Chrome開發者工具觀察插件的網路請求,我們發現插件會請求一個地址如gstatic.com/prettyearth的JSON文件,文件中包含了經過Base64的圖片內容,觀察發現,圖片的ID範圍大致在1000-8000之間,我們的爬蟲就要來爬取這些精美的背景圖片。

實現主要邏輯

由於爬取目標是JSON文件,爬蟲的主要邏輯就變成了爬取JSON-->提取圖片-->保存圖片

requests是一個常用的http請求庫,但是由於requests的請求都是同步的,我們使用aiohttp這個非同步http請求庫來代替。

async def fetch_image_by_id(item_id): url = fhttps://www.gstatic.com/prettyearth/assets/data/v2/{item_id}.json # 由於URL是https的,所以選擇不驗證SSL async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(verify_ssl=False)) as session: async with session.get(url) as response: # 獲取後需要將JSON字元串轉為對象 try: json_obj = json.loads(await response.text()) except json.decoder.JSONDecodeError as e: print(fDownload failed - {item_id}.jpg) return # 獲取JSON中的圖片內容欄位,經過Base64解碼成二進位內容 image_str = json_obj[dataUri].replace(data:image/jpeg;base64,, ) image_data = base64.b64decode(image_str) save_folder = dir_path = os.path.dirname( os.path.realpath(__file__)) + /google_earth/ with open(f{save_folder}{item_id}.jpg, wb) as f: f.write(image_data) print(fDownload complete - {item_id}.jpg)

aiohttp基於asyncio,所以在調用時需要使用async/await語法糖,可以看到,由於aiohttp中提供了一個ClientSession上下文,代碼中使用了async with的語法糖。

加入並行邏輯

上面的代碼是抓取單張圖片的邏輯,批量抓取圖片,需要再嵌套一層方法:

async def fetch_all_images(): # 使用Semaphore限制最大並發數 sem = asyncio.Semaphore(10) ids = [id for id in range(1000, 8000)] for current_id in ids: async with sem: await fetch_image_by_id(current_id)

接下來,將這個方法加入到asyncio的事件循環中。

event_loop = asyncio.get_event_loop()future = asyncio.ensure_future(fetch_all_images())results = event_loop.run_until_complete(future)

使用uvloop加速

uvloop基於libuv,libuv是一個使用C語言實現的高性能非同步I/O庫,uvloop用來代替asyncio默認事件循環,可以進一步加快非同步I/O操作的速度。

uvloop的使用非常簡單,只要在獲取事件循環前,調用如下方法,將asyncio的事件循環策略設置為uvloop的事件循環策略。

asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

使用上面的代碼,我們可以快速將大約1500張的圖片爬取下來。

性能對比

為了驗證aiohttp和uvloop的性能,筆者使用requests+concurrent庫實現了一個多進程版的爬蟲,分別爬取20個id,消耗的時間如圖。

可以看到,耗時相差了大概7倍,aiohttp+uvloop的組合在爬蟲這種I/O密集型的場景下,可以說具有壓倒性優勢。相信在不遠的將來,基於asyncio的庫會將無數爬蟲工程師從加班中拯救出來。

掃碼關注Python私房菜

微信搜索【Python私房菜】加關注,拯救加班的你

推薦閱讀:

TAG:Python | 爬蟲計算機網路 | python爬蟲 |