Aiohttp:Client
Make a Request
Begin by importing the aiohttp module, and asyncio:
Now, let’s try to get a web-page. For example let’s query http://httpbin.org/get:
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())
asyncio.run(main())
Now, we have a ClientSession called session and a ClientResponse object called resp. We can get all the information we need from the response. The mandatory parameter of ClientSession.get() coroutine is an HTTP url (str or class:yarl.URL instance).
In order to make an HTTP POST request use ClientSession.post() coroutine:
Other HTTP methods are available as well:
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')
To make several requests to the same site more simple, the parameter base_url of ClientSession constructor can be used. For example to request different endpoints of http://httpbin.org can be used the following code:
async with aiohttp.ClientSession('http://httpbin.org') as session:
async with session.get('/get'):
pass
async with session.post('/post', data=b'data'):
pass
async with session.put('/put', data=b'data'):
pass
Note |
Don’t create a session per request. Most likely you need a session per application which performs all requests together. More complex cases may require a session per site, e.g. one for Github and other one for Facebook APIs. Anyway making a session for every request is a very bad idea. A session contains a connection pool inside. Connection reusage and keep-alives (both are on by default) may speed up total performance. |
A session context manager usage is not mandatory but await session.close() method should be called in this case, e.g.:
POST a Multipart-Encoded File
To upload Multipart-encoded files:
url = 'http://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}
await session.post(url, data=files)
You can set the filename and content_type explicitly:
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)
Streaming uploads
aiohttp supports multiple types of streaming uploads, which allows you to send large files without reading them into memory.
As a simple case, simply provide a file-like object for your body:
Or you can use asynchronous generator:
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())
Because the content attribute is a StreamReader (provides async iterator protocol), you can chain get and post requests together:
resp = await session.get('http://python.org')
await session.post('http://httpbin.org/post',
data=resp.content)
WebSockets
aiohttp works with client websockets out-of-the-box.
You have to use the aiohttp.ClientSession.ws_connect()
coroutine for client websocket connection. It accepts a url as a first parameter and returns ClientWebSocketResponse, with that object you can communicate with websocket server using response’s methods:
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
You must use the only websocket task for both reading (e.g. await ws.receive() or async for msg in ws:) and writing but may have multiple writer tasks which can only send data asynchronously (by await ws.send_str('data') for example).