使用协程的异步请求以其低时消耗和对硬件的高利用而著称,翻看了很多论坛,发现协程在进行爬虫以及高频网络请求时的耗时比单多进程和单多线程还要好。本文将使用requests和使用aiohttp+asyncio进行比较,比较一下具体使用协程和不使用协程能差距多少。
本文测试所使用目标网址是廖雪峰老师python3教程的评论页 目前一共有2318页。
1.使用requests获取单个网址
import requests,time
r=requests.get(url="https://www.liaoxuefeng.com/discuss/001409195742008d822b26cf3de46aea14f2b7378a1ba91000?page=1")
print(len(r.text)) #结果 40007 用时0.3s
import aiohttp, asyncio
async def fn():
async with aiohttp.get(url='https://www.liaoxuefeng.com/discuss/001409195742008d822b26cf3de46aea14f2b7378a1ba91000?page=1') as resp:
text=await resp.text()
print(len(text))
loop = asyncio.get_event_loop()
loop.run_until_complete(fn()) #结果 40007 用时0.3s
3.使用requests获取50个网址
import requests
a=[len(requests.get(url="https://www.liaoxuefeng.com/discuss/001409195742008d822b26cf3de46aea14f2b7378a1ba91000?page={}".format(i)).text) for i in range(1,51)]
print(a) #结果 [40007, 40444, 40367, 40820, 40534, 40505, 40735, 40454, 40768, 40636, 40600, 40888, 41277, 41390, 41222, 40899, 40853, 40616, 40654, 40870, 41249, 40840, 40782, 41326, 41136, 40511, 40504, 40609, 41038, 41054, 40486, 40556, 41083, 40975, 40861, 40877, 40166, 40899, 40598, 40920, 40902, 40994, 40735, 40714, 41064, 40719, 40991, 40748, 40652, 40799]
用时:5.3s
4.使用aiohttp+asyncio获取50个网址
import aiohttp, asyncio
async def fn(num):
async with aiohttp.get(url='https://www.liaoxuefeng.com/discuss/001409195742008d822b26cf3de46aea14f2b7378a1ba91000?page={}'.format(num)) as resp:
text=await resp.text()
result.append(len(text))
result=[]
loop = asyncio.get_event_loop()
tasks=[asyncio.ensure_future(fn(i)) for i in range(1,51)]
loop.run_until_complete(asyncio.wait(tasks))
print(result)
#结果:[40636, 41326, 40853, 41277, 41249, 40735, 40616, 40454, 40888, 40899, 40007, 40768, 40486, 40870, 40820, 40444, 40367, 41136, 40609, 40975, 40504, 40166, 40920, 40598, 40556, 40652, 41083, 40735, 40799, 40899, 40902, 40748, 41064, 40505, 40654, 40511, 40600, 40534, 41390, 40782, 41222, 40840, 41038, 40714, 41054, 40877, 40719, 40991, 40861, 40994]
#用时:3.4s
5.发送2318个请求
既然页面总共有2318个,我们不如全部使用爬一下,老方法requests:
import requests
a=[len(requests.get(url="https://www.liaoxuefeng.com/discuss/001409195742008d822b26cf3de46aea14f2b7378a1ba91000?page={}".format(i)).text) for i in range(1,2319)]
print(a)
#用时:497s
由于同时并发2318个请求并交给网卡去处理会出现负荷过重,aiohttp默认同时最大支持1024个协程的进行,但是考虑到硬件的承受能力,我们采用维护一个“协程池”的方法。使用 asyncio.Semaphore来控制同时进行的最大IO量。每当有一个协程完成时,便加入一个新的协程:
import aiohttp, asyncio
async def fn(num,sem):
async with sem:
async with aiohttp.get(url="https://www.liaoxuefeng.com/discuss/001409195742008d822b26cf3de46aea14f2b7378a1ba91000?page={}".format(num)) as resp:
text=await resp.text()
result.append(len(text))
loop=asyncio.get_event_loop()
result=[]
sem=asyncio.Semaphore(100) #维持100个信号量
tasks=[ asyncio.ensure_future(fn(i,sem)) for i in range(1,2319)]
loop.run_until_complete(asyncio.wait(tasks))
print(result)
#用时 预计200s左右
在用async测试的过程中,可能并发的请求服务器压力太大,廖雪峰老师的网站出现了504网关超时报错,第二天测试的时候,刚进行到一半服务器又崩溃了,服务器应该没做优化,这样一点并发流量怎么就垮了。
另外一篇将会使用 gevent+requsets 和 aiohttp+asyncio 的方式进行比较,两个都是协程模型。