aiohttp+uvloop的组合爬虫I/O密集型的场景

准备工作

Earth View from Google Earth是一款Chrome插件,会在打开新标签页时自动加载一张来自Google Earth的背景图片。

e10c61800ab736115a82b647a81f4977

 

ssets/data/v2/1234.json的JSON文件,文件中包含了经过Base64的图片内容,观察发现,图片的ID范围大致在1000-8000之间,我们的爬虫就要来爬取这些精美的背景图片。

实现主要逻辑

由于爬取目标是JSON文件,爬虫的主要逻辑就变成了爬取JSON–>提取图片–>保存图片。

requests是一个常用的http请求库,但是由于requests的请求都是同步的,我们使用aiohttp这个异步http请求库来代替。

efd7ccdceff1f81dba4ce15fb7cc66f9

 

aiohttp基于asyncio,所以在调用时需要使用async/await语法糖,可以看到,由于aiohttp中提供了一个ClientSession上下文,代码中使用了async with的语法糖。

加入并行逻辑

上面的代码是抓取单张图片的逻辑,批量抓取图片,需要再嵌套一层方法:

1345a448684d743f3fa0fa2b0029f6a8

 

接下来,将这个方法加入到asyncio的事件循环中。

95cd77ffd5b286d80168d6876db2771d

 

使用uvloop加速

uvloop基于libuv,libuv是一个使用C语言实现的高性能异步I/O库,uvloop用来代替asyncio默认事件循环,可以进一步加快异步I/O操作的速度。

uvloop的使用非常简单,只要在获取事件循环前,调用如下方法,将asyncio的事件循环策略设置为uvloop的事件循环策略。

asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

使用上面的代码,我们可以快速将大约1500张的图片爬取下来。

ba01ffa2fd9379de4d6685b206adce74

 

性能对比

为了验证aiohttp和uvloop的性能,笔者使用requests+concurrent库实现了一个多进程版的爬虫,分别爬取20个id,消耗的时间如图。

688177fdef5268ff3c8d343ad1a9dd4e

 

可以看到,耗时相差了大概7倍,aiohttp+uvloop的组合在爬虫这种I/O密集型的场景下,可以说具有压倒性优势。相信在不远的将来,基于asyncio的库会将无数爬虫工程师从加班中拯救出来。