异步请求库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-datax00' # 未经编码的数据通过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(main2())
asyncio.run(main3())
asyncio.run(main4())
6.设置请求超时
ClientTimeout类有四个类属性:
- total:整个操作时间包括连接建立,请求发送和响应读取。
- connect:该时间包括建立新连接或在超过池连接限制时等待池中的空闲连接的连接。
- sock_connect:连接到对等点以进行新连接的超时,不是从池中给出的。
- sock_read:从对等体读取新数据部分之间的时间段内允许的最大超时。
import aiohttp
import asyncio
timeout = aiohttp.ClientTimeout(total=60)
async def main():
async with aiohttp.ClientSession(timeout=timeout) as sess:
async with sess.get('http://httpbin.org/get') as resp:
print(resp.status)
print(await resp.text())
if __name__ == "__main__":
asyncio.run(main())
7.自定义cookie
import aiohttp
import asyncio
cookies = {'cookies_are': 'working'}
async def main():
async with aiohttp.ClientSession(cookies=cookies) as session:
async with session.get('http://httpbin.org/cookies') as resp:
print(resp.status)
print(await resp.text())
assert await resp.json() == {"cookies": {"cookies_are": "working"}}
if __name__ == "__main__":
asyncio.run(main())
8.在多个请求之间共享cookie
import aiohttp
import asyncio
async def main():
async with aiohttp.ClientSession() as session:
await session.get('http://httpbin.org/cookies/set?my_cookie=my_value')
filtered = session.cookie_jar.filter_cookies('http://httpbin.org')
print(filtered)
assert filtered['my_cookie'].value == 'my_value'
async with session.get('http://httpbin.org/cookies') as r:
json_body = await r.json()
print(json_body)
assert json_body['cookies']['my_cookie'] == 'my_value'
if __name__ == "__main__":
asyncio.run(main())
9.Cookie的安全性
默认ClientSession使用的是严格模式的 aiohttp.CookieJar. RFC 2109,明确的禁止接受url和ip地址产生的cookie,只能接受 DNS 解析IP产生的cookie。
可以通过设置aiohttp.CookieJar 的 unsafe=True 来配置
jar = aiohttp.CookieJar(unsafe=True)
session = aiohttp.ClientSession(cookie_jar=jar)
10.自定义请求头
async with aiohttp.ClientSession(headers={'User-Agent': 'your agent'}) as session:
async with session.get('http://httpbin.org/headers') as resp:
print(resp.status)
print(await resp.text())
11.SSL验证
默认情况下,aiohttp对HTTPS协议使用严格检查,如果你不想上传SSL证书,可将ssl设置为False。
r = await session.get('https://example.com', ssl=False)
12.代理
使用无授权代理
async with aiohttp.ClientSession() as session:
async with session.get("http://python.org", proxy="http://proxy.com") as resp:
print(resp.status)
代理授权的两种方式
# 第一种
async with aiohttp.ClientSession() as session:
proxy_auth = aiohttp.BasicAuth('user', 'pass')
async with session.get("http://python.org", proxy="http://proxy.com", proxy_auth=proxy_auth) as resp:
print(resp.status)
# 第二种
session.get("http://python.org", proxy="http://user:pass@some.proxy.com")