aiohttp
简介
aiohttp
可以实现单线程并发IO操作,用他来代替非异步模块request来发送请求,请求中的ua,headers,和参数都可以添加,添加方法如下:
环境安装
pip install aiohttp
aiohttp使用
1.发起请求
async def fetch(): async with aiohttp.ClientSession() as session: async with session.get('https://www.baidu.com') as resposne: print(await resposne.text()) loop = asyncio.get_event_loop() tasks = [fetch(),] loop.run_until_complete(asyncio.wait(tasks))
2.添加请求参数的方法:
params = {'key': 'value', 'page': 10} async def fetch(): async with aiohttp.ClientSession() as session: async with session.get('https://www.baidu.com/s',params=params) as resposne: print(await resposne.url) loop = asyncio.get_event_loop() tasks = [fetch(),] loop.run_until_complete(asyncio.wait(tasks))
3.UA伪装的添加方法:
url = 'http://httpbin.org/user-agent' headers = {'User-Agent': 'test_user_agent'} async def fetch(): async with aiohttp.ClientSession() as session: async with session.get(url,headers=headers) as resposne: print(await resposne.text()) loop = asyncio.get_event_loop() tasks = [fetch(),] loop.run_until_complete(asyncio.wait(tasks))
4.自定义cookies的方法:
url = 'http://httpbin.org/cookies' cookies = {'cookies_name': 'test_cookies'} async def fetch(): async with aiohttp.ClientSession() as session: async with session.get(url,cookies=cookies) as resposne: print(await resposne.text()) loop = asyncio.get_event_loop() tasks = [fetch(),] loop.run_until_complete(asyncio.wait(tasks))
5.post请求参数
url = 'http://httpbin.org' payload = {'username': 'zhang', 'password': '123456'} async def fetch(): async with aiohttp.ClientSession() as session: async with session.post(url, data=payload) as resposne: print(await resposne.text()) loop = asyncio.get_event_loop() tasks = [fetch(), ] loop.run_until_complete(asyncio.wait(tasks))
6.设置代理
url = "http://python.org" async def fetch(): async with aiohttp.ClientSession() as session: async with session.get(url, proxy="http://some.proxy.com") as resposne: print(resposne.status) loop = asyncio.get_event_loop() tasks = [fetch(), ] loop.run_until_complete(asyncio.wait(tasks))
异步IO处理
# 环境安装:pip install aiohttp # 使用该模块中的ClientSession import requests import asyncio import time import aiohttp start = time.time() urls = [ 'http://127.0.0.1:5000/tiger','http://127.0.0.1:5000/jay','http://127.0.0.1:5000/tom', 'http://127.0.0.1:5000/tiger', 'http://127.0.0.1:5000/jay', 'http://127.0.0.1:5000/tom', 'http://127.0.0.1:5000/tiger', 'http://127.0.0.1:5000/jay', 'http://127.0.0.1:5000/tom', 'http://127.0.0.1:5000/tiger', 'http://127.0.0.1:5000/jay', 'http://127.0.0.1:5000/tom', ] async def get_page(url): async with aiohttp.ClientSession() as session: #get()、post(): #headers,params/data,proxy='http://ip:port' async with await session.get(url) as response: #text()返回字符串形式的响应数据 #read()返回的二进制形式的响应数据 #json()返回的就是json对象 #注意:获取响应数据操作之前一定要使用await进行手动挂起 page_text = await response.text() print(page_text) tasks = [] for url in urls: c = get_page(url) task = asyncio.ensure_future(c) tasks.append(task) loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(tasks)) end = time.time() print('总耗时:',end-start)
# 使用aiohttp替代requests模块 import time import asyncio import aiohttp async def get_page(url): async with aiohttp.ClientSession() as session: # 只要有耗时就会有阻塞,就得使用await进行挂起操作 async with await session.get(url=url) as response: page_text = await response.text() # 二进制read()/json() print('响应数据', page_text) start = time.time() urls = [ 'http://127.0.0.1:5000/tiger', 'http://127.0.0.1:5000/jay', 'http://127.0.0.1:5000/tom', ] loop = asyncio.get_event_loop() tasks = [] for url in urls: cone = get_page(url) task = asyncio.ensure_future(cone) tasks.append(task) loop.run_until_complete(asyncio.wait(tasks)) print('总耗时: ', time.time()-start)
在这里我们将请求库由 requests 改成了 aiohttp,通过 aiohttp 的 ClientSession 类的 get() 方法进行请求,结果如下:
Hello tom Hello jay Hello tiger Hello tiger Hello jay Hello tiger Hello tom Hello jay Hello jay Hello tom Hello tom Hello tiger 总耗时: 2.037203073501587
成功了!我们发现这次请求的耗时由 6秒变成了 2 秒,耗时直接变成了原来的 1/3。
代码里面我们使用了 await,后面跟了 get() 方法,在执行这五个协程的时候,如果遇到了 await,那么就会将当前协程挂起,转而去执行其他的协程,直到其他的协程也挂起或执行完毕,再进行下一个协程的执行。
开始运行时,时间循环会运行第一个 task,针对第一个 task 来说,当执行到第一个 await 跟着的 get() 方法时,它被挂起,但这个 get() 方法第一步的执行是非阻塞的,挂起之后立马被唤醒,所以立即又进入执行,创建了 ClientSession 对象,接着遇到了第二个 await,调用了 session.get() 请求方法,然后就被挂起了,由于请求需要耗时很久,所以一直没有被唤醒,好第一个 task 被挂起了,那接下来该怎么办呢?事件循环会寻找当前未被挂起的协程继续执行,于是就转而执行第二个 task 了,也是一样的流程操作,直到执行了第五个 task 的 session.get() 方法之后,全部的 task 都被挂起了。所有 task 都已经处于挂起状态,那咋办?只好等待了。3 秒之后,几个请求几乎同时都有了响应,然后几个 task 也被唤醒接着执行,输出请求结果,最后耗时,3 秒!
怎么样?这就是异步操作的便捷之处,当遇到阻塞式操作时,任务被挂起,程序接着去执行其他的任务,而不是傻傻地等着,这样可以充分利用 CPU 时间,而不必把时间浪费在等待 IO 上。
可见,使用了异步协程之后,我们几乎可以在相同的时间内实现成百上千倍次的网络请求,把这个运用在爬虫中,速度提升可谓是非常可观了。
如何实现数据解析--任务的绑定回调机制(完整的协程过程)
import time import asyncio import aiohttp # 回调函数: 主要用来解析响应数据 def callback(task): print('This is callback') # 获取响应数据 page_text = task.result() print("接下来就可以在回调函数中实现数据解析") async def get_page(url): async with aiohttp.ClientSession() as session: # 只要有耗时就会有阻塞,就得使用await进行挂起操作 async with await session.get(url=url) as response: page_text = await response.text() # 二进制read()/json() print('响应数据', page_text) return page_text start = time.time() urls = [ 'http://127.0.0.1:5000/tiger', 'http://127.0.0.1:5000/jay', 'http://127.0.0.1:5000/tom', ]
#第一步产生事件循环对象 loop = asyncio.get_event_loop()
#任务列表 tasks = [] for url in urls: cone = get_page(url)
#第二步将协程函数对象放进任务中 task = asyncio.ensure_future(cone) # 给任务对象绑定回调函数用于解析响应数据 task.add_done_callback(callback)
#第三步将所有的任务添加到任务列表中 tasks.append(task) #第四步运行事件循环对象,asyncio.wait()来实现多任务自动在循环中运行 loop.run_until_complete(asyncio.wait(tasks)) print('总耗时: ', time.time()-start)