异步请求库aiohttp的使用
1.使用aiohttp发起一个请求
官方推荐使用一个客户端会话来发起所有请求,会话中记录了请求的cookie,但你还可以使用aiohttp.request来发送请求。
当我们使用 async def 就是定义了一个异步函数,异步逻辑由asyncio提供支持。
async with aiohttp.ClientSession() as session 为异步上下文管理器,在请求结束时或者发生异常请求时支持自动关闭实例化的客户端会话。
import asyncio import aiohttp async def main(): async with aiohttp.ClientSession() as session: async with session.get('http://httpbin.org/get') as resp: print(resp.status) print(await resp.text()) if __name__ == '__main__': # python3.7才支持这种写法,作为一个入口函数,以debug模式运行事件循环 asyncio.run(main(), debug=True) # python3.6及以下版本写法 event_loop = asyncio.get_event_loop() results = event_loop.run_until_complete(asyncio.gather(main())) event_loop.close()
关于asyncio的一些关键字的说明:
- event_loop 事件循环:程序开启一个无限循环,把一些函数注册到事件循环上,当满足事件发生的时候,调用相应的协程函数。
- coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。
- task 任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含了任务的各种状态。
- future: 代表将来执行或没有执行的任务的结果。它和task上没有本质上的区别。
- async/await 关键字:python3.5用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。
2.发送post请求
除了常用的get和post请求还有以下几种请求方式:
- put:
session.put('http://httpbin.org/put', data=b'data')
- delete:
session.delete('http://httpbin.org/delete')
- head:
session.head('http://httpbin.org/get')
- options:
session.options('http://httpbin.org/get')
- patch:
session.patch('http://httpbin.org/patch', data=b'data')
import asyncio import aiohttp async def main(): data = b'\x00Binary-data\x00' # 未经编码的数据通过bytes数据上传 data = 'text' # 传递文本数据 data = {'key': 'value'} # 传递form表单 async with aiohttp.ClientSession() as sess: async with sess.post('http://httpbin.org/post', data=data) as resp: print(resp.status) print(await resp.text()) # 复杂的post请求 async def main2(): pyload = {'key': 'value'} # 传递pyload async with aiohttp.ClientSession() as sess: async with sess.post('http://httpbin.org/post', json=pyload) as resp: print(resp.status) print(await resp.text()) if __name__ == '__main__': asyncio.run(main()) asyncio.run(main2())
3.向URL中传递参数
import asyncio import aiohttp async def main(): """以下三种方式均可以""" params = {'key1': 'value1', 'key2': 'value2'} params = [('key', 'value1'), ('key', 'value2')] params = 'key=value+1' async with aiohttp.ClientSession() as sess: async with sess.get('http://httpbin.org/get', params=params) as resp: print(resp.status) print(await resp.text()) if __name__ == '__main__': asyncio.run(main())
4.响应和状态码
由于获取响应内容是一个阻塞耗时过程,所以我们使用await实现协程切换
- resp.history 查看重定向历史
- resp.headers 获取响应头
- resp.cookies 获取响应cookie
- resp.status 获取响应状态码
- resp.text(encoding='utf-8) 获取响应文本
- resp.json() 获取json响应数据
- resp.read() 获取二进制响应数据
- resp.content.read(100) 获取流响应,每次获取100个字节
import asyncio import aiohttp async def main(): params = {'key1': 'value1', 'key2': 'value2'} async with aiohttp.ClientSession() as sess: async with sess.get('http://httpbin.org/get', params=params) as resp: print(resp.status) # 状态码 print(await resp.text(encoding='utf-8')) # 文本响应,可以设置响应编码,默认是utf-8 print(await resp.json()) # json响应 print(await resp.read()) # 二进制响应,适用于下载图片等 async def main2(): async with aiohttp.ClientSession() as sess: async with sess.get('https://api.github.com/events') as resp: print(resp.status) # 状态码 print(await resp.content.read(100)) # 流响应,适用于大文件,分次读取 # await生成一个迭代器,通过不断循环拿到每一次100个字节码 while True: chunk = await resp.content.read(100) print(chunk) if not chunk: break if __name__ == '__main__': asyncio.run(main()) asyncio.run(main2())
5.向目标服务器上传文件
import asyncio import aiohttp async def main(): """传递文件""" files = {'file': open('report.xls', 'rb')} async with aiohttp.ClientSession() as sess: async with sess.post('http://httpbin.org/post', data=files) as resp: print(resp.status) print(await resp.text()) async def main2(): """实例化FormData可以指定filename和content_type""" data = aiohttp.FormData() data.add_field('file', open('report.xls', 'rb'), filename='report.xls', content_type='application/vnd.ms-excel') async with aiohttp.ClientSession() as sess: async with sess.post('http://httpbin.org/post', data=data) as resp: print(resp.status) print(await resp.text()) async def main3(): """流式上传文件""" async with aiohttp.ClientSession() as sess: with open('report.xls', 'rb') as f: async with sess.post('http://httpbin.org/post', data=f) as resp: print(resp.status) print(await resp.text()) async def main4(): """因为content属性是 StreamReader(提供异步迭代器协议),所以您可以将get和post请求链接在一起。python3.6及以上才能使用""" async with aiohttp.ClientSession() as sess: async with sess.get('http://python.org') as resp: async with sess.post('http://httpbin.org/post', data=resp.content) as r: print(r.status) print(await r.text()) if __name__ == '__main__': asyncio.run(main()) asyncio.run