首先导入库
import aiohttp
import asyncio
然后,让我们尝试获取一个网页页面。例如,访问 http://httpbin.org/get,运行以下代码,可以获得访问的数据:
import aiohttp
import asyncio
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())
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.run_forever()
现在,我们有了一个客户端时域称作session,有了一个客户端响应称作resp,我们可以从响应中得到我们想要的信息,ClientSession.get()协程的参数是这个HTTP url(即上面中的url)
为了使用HTTP的post请求,使用协程ClientSession.post():
session.post('http://httpbin.org/post', data=b'data')
其他的HTTP方法也是可以使用的:
session.put('http://httpbin.org/put', data=b'data')
session.delete('http://httpbin.org/delete')
session.head('http://httpbin.org/get')
session.options('http://httpbin.org/get')
session.patch('http://httpbin.org/patch', data=b'data')
注意:
请不要每个request都创建一个session,这是一个不好的习惯,应该一个session对应多个request。每一个session都包含一个连接,复用连接可以提高速度与效率。
一个session上下文管理器不是必须的,但是在下面这种情况下是需要的:
session = aiohttp.ClientSession()
async with session.get('...'):
# ...
await session.close()
例如,如果你想传递key1=value1和key2=value2到httpbin.org/get,你可以使用一个字典通过以下代码实现:
params = {'key1': 'value1', 'key2': 'value2'}
async def main():
async with aiohttp.ClientSession() as session:
async with session.get('http://httpbin.org/get',params=params) as resp:
expect1 = 'http://httpbin.org/get?key2=value2&key1=value1'
if str(resp.url) == expect1:
print(1)
else:
print(0)
expect2 = 'http://httpbin.org/get?key1=value1&key2=value2'
if str(resp.url) == expect2:
print(1)
else:
print(0)
也可以传递一个元组列表作为参数,代码如下:
params = [('key', 'value1'), ('key', 'value2')]
async def main():
async with aiohttp.ClientSession() as session:
async with session.get('http://httpbin.org/get',
params=params) as r:
expect = 'http://httpbin.org/get?key=value1&key=value2'
if str(r.url) == expect:
print(1)
else:
print(0)
也可以使用一个字符串作为参数,代码如下:
async def main():
async with aiohttp.ClientSession() as session:
async with session.get('http://httpbin.org/get',
params='key=value+1') as r:
if str(r.url) == 'http://httpbin.org/get?key=value+1':
print(1)
else:
print(0)
注意:
在aiohttp中,url请求前会自动进行url标准化,如果不需要标准化,可以设置以下格式(encoded=True):
await session.get(URL('http://example.com/%30', encoded=True))
警告:
parms与encoded参数不能同时设置
我们可以读取服务器的响应内容和其状态代码:
async with session.get('https://api.github.com/events') as resp:
print(resp.status)
print(await resp.text())
aiohttp会自动解码从服务器读得的数据,如果你想具体指定习惯的编码方式,可以采取以下代码:
await resp.text(encoding='windows-1251')
你可以将响应主体设置为字节形式,以用作非文本请求:
print(await resp.read())
任何session的请求方式如 request(), ClientSession.get(), ClientSesssion.post()等都接受json参数:
async with aiohttp.ClientSession() as session:
async with session.post(url, json={'test': 'object'})
默认情况下session使用python的标准json模块来进行序列化,但是这里可以使用不同的序列化器,ClientSession()接受json序列化参数:
import ujson
async with aiohttp.ClientSession(
json_serialize=ujson.dumps) as session:
await session.post(url, json={'test': 'object'})
注意:ujson库比标准库json快但是略微互斥
如果你要处理JSON数据,也有一个内嵌的JSON解码器供你使用:
async with session.get('https://api.github.com/events') as resp:
print(await resp.json())
如果JSON解码失败,json()会提出一个例外,可以指定编码解码方式给json()
注意:以上方法会将全部响应数据读入内存,如果你计划读取大量数据,考虑使用下面的流响应方式
虽然 read(), json 和 text() 方法很方便,你需要小心使用他们,所有的这些方法都将全部响应数据读入内存。例如,如果你要下载几十亿字节的文件,这些方法会将数据全部导入内存空间。
除非,你使用了content属性,它是aiohttp.StreamReader类的实例化。gzip和deflate传输编码会自动为你解码:
async with session.get('https://api.github.com/events') as resp:
await resp.content.read(10)
但是,一般来说,你应该使用这样的模式来保存流式传输到文件中的内容:
with open(filename, 'wb') as fd:
while True:
chunk = await resp.content.read(chunk_size)
if not chunk:
break
fd.write(chunk)
在从content显式读取数据后,不可以使用 read() , json() and text()
通常,当你想发送一些表单编码的数据——例如一个HTML表单。为了做到这点,只要传送一个字典给data参数,你的字典数据会自动被表单编码当请求发出时:
payload = {'key1': 'value1', 'key2': 'value2'}
async with session.post('http://httpbin.org/post',
data=payload) as resp:
print(await resp.text())
{
...
"form": {
"key2": "value2",
"key1": "value1"
},
...
}
如果你想发送一个不是表单编码的数据,你可以传送一个字节类数据而不是字典:
async with session.post(url, data=b'\x00Binary-data\x00') as resp:
...
如果你想要发送JSON数据:
async with session.post(url, json={'example': 'test'}) as resp:
...
发送正确的文本类型,只需使用文本属性:
async with session.post(url, data='Тест') as resp:
...
上传一个多部分编码文件:
url = 'http://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}
await session.post(url, data=files)
你还可以设置文件名及content_type:
url = 'http://httpbin.org/post'
data = FormData()
data.add_field('file',
open('report.xls', 'rb'),
filename='report.xls',
content_type='application/vnd.ms-excel')
await session.post(url, data=data)
如果你传递了一个文件作为data参数,aiohttp会自动以流的方式上传到服务器
aiohttp支持多种流式上传,让你不用在上传大文件时将他们读入内存
简单来说,只需要提供一个类似文件的对象:
with open('massive-body', 'rb') as f:
await session.post('http://httpbin.org/post', data=f)
或者你可以使用一个异步生成器:
async def file_sender(file_name=None):
async with aiofiles.open(file_name, 'rb') as f:
chunk = await f.read(64*1024)
while chunk:
yield chunk
chunk = await f.read(64*1024)
# Then you can use file_sender as a data provider:
async with session.post('http://httpbin.org/post',
data=file_sender(file_name='huge_file')) as resp:
print(await resp.text())
因为content属性是StreamReader(提供异步迭代器协议),所以可以将get和post请求链接在一起:
resp = await session.get('http://python.org')
await session.post('http://httpbin.org/post',
data=resp.content)
你需要使用 aiohttp.ClientSession.ws_connect() 协程去完成客户端WebSocket连接,它接受一个url作为第一个参数并返回ClientWebSocketResponse,通过此对象你可以使用响应方法和websocket交流:
async with session.ws_connect('http://example.org/ws') as ws:
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
if msg.data == 'close cmd':
await ws.close()
break
else:
await ws.send_str(msg.data + '/answer')
elif msg.type == aiohttp.WSMsgType.ERROR:
break
超时暂停设置在ClientTimeout这个数据结构中
默认情况下aiohttp使用一个5分钟的超时暂停,这意味着所有操作都必须在五分钟内完成
可以通过超时参数覆盖此值:
timeout = aiohttp.ClientTimeout(total=60)
async with aiohttp.ClientSession(timeout=timeout) as session:
...
也可以在一个请求中覆盖此值:
async with session.get(url, timeout=timeout) as resp:
...
支持的ClientTimeout字段有:
total:
全部的操作时间,包括连接完成,发送请求和读取响应
connect:
时间包括有为新连接建立连接,或者超过池连接限制时
等待空闲连接
sock_connect:
超时暂停设置给对等网络的新连接,而不是从池中
sock_read:
从对等机读取新的数据之间运行的最大超时时间
更多详细请参考 ClientTimeout
默认设置为:
aiohttp.ClientTimeout(total=5*60, connect=None,
sock_connect=None, sock_read=None)
finished