网址

  1. aiohttp中文文档

核心功能

aiohttp是一个基于asyncio,为Python提供异步HTTP 客户端/服务端编程异步库。

1
pip install aiohttp

安装更快的cchardet库来代替chardet进行解码:

1
pip install cchardet

更快的客户端API DNS解析方案aiodns

1
pip install aiodns

下面两段代码功能是一样的:

1
2
async def coro(...):
ret = await f()
1
2
3
@asyncio.coroutine
def coro(...):
ret = yield from f()

快速开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 客户端
import aiohttp
import asyncio
import async_timeout

async def fetch(session, url):
with async_timeout.timeout(10):
async with session.get(url) as response:
return await response.text()

async def main():
async with aiohttp.ClientSession() as session:
html = await fetch(session, 'http://python.org')
print(html)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
1
2
3
4
5
6
7
8
9
10
11
12
13
# 服务端
from aiohttp import web

async def handle(request):
name = request.match_info.get('name', "Anonymous")
text = "Hello, " + name
return web.Response(text=text)

app = web.Application()
app.router.add_get('/', handle)
app.router.add_get('/{name}', handle)

web.run_app(app)

APIs

ClientSession类

ClientSession类用于创建客户端会话以及发起请求

1
2
class aiohttp.ClientSession(*, connector=None, loop=None, cookies=None, headers=None, skip_auto_headers=None, auth=None, json_serialize=json.dumps, version=aiohttp.HttpVersion11, cookie_jar=None, read_timeout=None, conn_timeout=None, raise_for_status=False, connector_owner=True, auto_decompress=True, proxies=None):
pass
  • connector (aiohttp.connector.BaseConnector) :
    基础连接器(BaseConnector)的子类实例,用于支持连接池。

  • loop:
    事件循环,用于处理HTTP请求. 一般用asyncio.get_event_loop()来获取事件循环。

  • skip_auto_headers:
    跳过会自动产生的headers值.

    • 如果没有显式传递,那么aiohttp会自动生成诸如User-Agent或Content-Type。
    • 使用该参数可以指定跳过,但注意Content-Length是不能跳过的。
  • auth(aiohttp.BasicAuth) :
    表示 HTTP基本认证的对象。

  • cookie_jar:
    AbstractCookieJar的实例。

    • 默认情况下每个会话实例都有自己的私有cookie jar用于自动处理cookies,
    • 但用户也可使用自己的jar重新定义其行为。
    • 在代理模式下不会处理cookies。
    • 如果不需要cookie处理,可以传递一个aiohttp.helper.DummyCookieJar实例。
  • json_serialize (可调用对象):
    可调用的Json序列化对象,默认是json.dumps()函数。

  • raise_for_status (布尔类型):
    每完成一个响应就自动调用 ClientResponse.raise_for_status(), 默认是False.

  • read_timeout (浮点数):
    请求操作的超时时间。
    read_timeout是累计自所有的请求操作(请求,重定向,响应,数据处理)。默认情况下是 5分钟。传递None或0来禁用超时检测。

  • conn_timeout (浮点数):
    建立连接的超时时间(可选)。0或None则禁用超时检测。

  • connector_owner (布尔类型):
    会话关闭时一同关闭连接器实例
    传递False让构造器允许在多个会话间共享连接池,不过cookies等信息是不共享的。

  • auto_decompress (布尔类型):
    自动解压响应体。

  • trust_env(布尔类型):
    若该参数设置为True则从环境变量HTTP_PROXY / HTTPS_PROXY中获取代理信息。

ClientResponse类

ClientSession.requests() 及其同类成员的返回对象。

  • version
    返回响应的版本信息,是HttpVersion实例对象。
  • status
    返回响应的HTTP状态码(整数),比如: 200。
  • reason
    返回响应的HTTP叙述(字符串),比如”OK”。
  • method
    返回请求的HTTP方法(字符串)。
  • url
    返回请求的URL(URL对象)。
  • connection
    返回处理响应的连接。
  • content
    包含响应主体(StreamReader)的载体流。支持多种读取方法。服务器使用分块传输编码时同样允许分块接受。
    读取时可能会抛出aiohttp.ClientPayloadError,这种情况发生在响应对象在接受所有的数据前就关闭了或有任何传输编码相关的错误如错误的压缩数据所造成的不规则分块编码。
  • cookies
    响应中的HTTP cookies,(由Set-Cookie HTTP头信息设置, 属于SimpleCookie)
  • headers
    返回响应的HTTP头信息,是一个大小写不敏感的并联字典(CIMultiDictProxy)。
  • raw_headers
    返回原始HTTP头信息,未经编码,格式是键值对形式。
  • content_type
    返回Content-Type头信息的内容。该属性只读。
  • charset
    返回请求的主体的编码。
    该值来自于Content-Type HTTP头信息。
    返回的值是类似于’utf-8’之类的字符串,如果HTTP头中不含Content-Type或其中没有charset信息则是None。
  • history
    返回包含所有的重定向请求(都是ClientResponse对象,最开始的请求在最前面)的序列,如果没用重定向则返回空序列。

ClientWebSocketResponse类

用于处理客户端websockets的类。使用协程方法aiohttp.ws_connect()或aiohttp.ClientSession.ws_connect()连接使用websocket的服务器,不要手动来创建ClientWebSocketResponse。

方法:

  • closed:
    如果close()已经调用过或接受了CLOSE消息则返回True。该属性只读。

  • protocol:
    调用start()后选择的websocket协议。
    如果服务器和客户端所选协议不一致则是None。

  • get_extra_info(name, default=None)
    返回从连接的transport中读取到的额外的信息。

  • exception()
    如果有错误则返回那个错误,否则返回None。

  • ping(message=b’’)
    向服务器发送PING.

    message参数:
    发送PING时携带的消息,类型是由UTF-8编码的字符串或字节码。(可选)

  • coroutine send_str(data)
    向服务器发送文本消息。

    data参数:
    字符串, 要发送的消息.如果不是字符串会抛出TypeError错误。

  • coroutine send_bytes(data)
    向服务器发送二进制消息。

    data参数:
    要发送的消息。如果不是字节码,字节数组或memoryview将抛出TypeError错误。

  • coroutine send_json(data, *, dumps=json.dumps)
    向服务器发送json字符串。

  • coroutine close(*, code=1000, message=b’’)
    用于向服务器发起挥手(CLOSE)信息,请求关闭连接。它会等待服务器响应。这是一个异步方法,所以如果要添加超时时间可以用asyncio.wait()或asyncio.wait_for()包裹。

    参数:
    code (整数) : 关闭状态码。
    message : pong消息携带的信息,类型是由UTF-8编码的字符串或字节码。

  • coroutine receive()
    等待服务器传回消息并在接受后将其返回。
    此方法隐式的处理PING, PONG, CLOSE消息。(不会返回这些消息)

    此方法返回WSMessage对象

  • coroutine receive_str()
    调用receive()并判断该消息是否是文本。

    此方法返回服务器传回的内容。

  • coroutine receive_bytes()
    调用receive()并判断该消息是否是二进制内容。

    此方法返回服务器传回的内容。 

  • coroutine receive_json(*, loads=json.loads)
    调用receive_str()并尝试将JSON字符串转成Python中的dict(字典)。

    此方法返回处理过的JSON内容。 

ws_connect类

创建一个websocket连接。返回ClientWebSocketResponse对象。

1
2
class ws_connect(url, *, protocols=(), timeout=10.0, receive_timeout=None, auth=None, autoclose=True, autoping=True, heartbeat=None, origin=None, proxy=None, proxy_auth=None, verify_ssl=None, fingerprint=None, ssl_context=None, proxy_headers=None, compress=0):
pass
  • protocols(元组) :
    websocket 协议。
  • timeout(浮点数) :
    超过超时时间后关闭websocket。默认是10秒。
  • receive_timeout(浮点数) :
    超过超时时间后不再从websocket接受消息。默认是None。
  • autoclose(布尔类型):
    默认为True。从服务器接受完消息后自动关闭websocket. 如果该参数为False,那么需要手动进行关闭
  • autoping(布尔类型) :
    当从服务器收到ping信息后自动回复pong信息。
  • heartbeat(浮点数) :
    每到心跳时间则发送ping信息并等待pong信息响应,如果没有收到pong信息则关闭连接。
  • origin(字符串) :
    发送到服务器的origin信息。

连接器类Connector

连接器用于支持aiohttp客户端API传输数据
这俩是标准连接器:

  1. TCPConnector 用于使用TCP连接(包括HTTP和HTTPS连接)。
  2. UnixConnector 用于使用UNIX连接(大多数情况下都用于测试的目的)。
    所有的连接器都应是BaseConnector的子类。
    默认情况下所有连接器都支持持久连接(keep-alive)(该行为由force_close参数控制)

BaseConnector

1
class aiohttp.BaseConnector(*, keepalive_timeout=15, force_close=False, limit=100, limit_per_host=0, enable_cleanup_closed=False, loop=None)
  • keepalive_timeout(浮点数) :
    释放后到再次使用的超时时间(可选)。禁用keep-alive可以传入0,或使用force_close=True。
  • limit(整数) :
    并发连接的总数。如果为None则不做限制。(默认为100)
  • limit_per_host :
    向同一个端点并发连接的总数。同一端点是具有相同 (host, port, is_ssl)信息的玩意 x 3! 如果是0则不做限制。(默认为0)
  • force_close(布尔类型) :
    连接释放后关闭底层网络套接字。(该参数可选)
  • enable_cleanup_closed (布尔类型) :
    一些SSL服务器可能会没有正确的完成SSL关闭过程,这种时候asyncio会泄露SSL连接。如果设置为True,aiohttp会在两秒后额外执行一次停止。此功能默认不开启。
  • loop :
    事件循环(event loop)

TCPConnector

1
class aiohttp.TCPConnector(*, verify_ssl=True, fingerprint=None, use_dns_cache=True, ttl_dns_cache=10, family=0, ssl_context=None, local_addr=None, resolver=None, keepalive_timeout=sentinel, force_close=False, limit=100, limit_per_host=0, enable_cleanup_closed=False, loop=None)

用于使用TCP处理HTTP和HTTPS的连接器。
TCPConnector继承于BaseConnector.
接受BaseConnector所需的所有参数和几个额外的TCP需要的参数。

UnixConnector

1
class aiohttp.UnixConnector(path, , conn_timeout=None, keepalive_timeout=30, limit=100, force_close=False, loop=None)

Unix 套接字连接器。
UnixConnector继承于BaseConnector。
使用UnixConnector发送HTTP/HTTPS请求。底层通过UNIX套接字传输。
UNIX套接字对于测试并快速在同一个主机上的进程间建立连接非常方便。

CookieJar类

cookie jar实例对象可用在ClientSession.cookie_jar中。
jar对象包含用来存储内部cookie数据的Morsel组件。

1
class aiohttp.CookieJar(*, unsafe=False, loop=None)
  • unsafe (布尔类型) – (可选)是否可从IP(的HTTP请求)中接受cookies。
  • loop (布尔类型) – 一个事件循环实例。
  1. 查看保存的cookies数量:

    1
    len(session.cookie_jar)
  2. 被迭代:

    1
    2
    3
    for cookie in session.cookie_jar:
    print(cookie.key)
    print(cookie["domain"])

方法:

  1. update_cookies(cookies, response_url=None)
    从服务器返回的Set-Cookie信息中更新cookies。

    • cookies – 需要collections.abc.Mapping对象(如dict, SimpleCookie) 或包含cookies的可迭代键值对对象。
    • response_url (字符串) – cookies所属的URL,如果要共享cookies则不要填。标准的cookies应是与服务器URL成对出现,只在向该服务器请求时被发送出去,如果是共享的则会发送给所有的服务器。
  2. filter_cookies(request_url)
    筛选cookies,返回与request_url相匹配的cookies,和能给这个URL携带的cookie(一般是设置为None也就是共享的cookie)。

    • response_url (字符串) – 需要筛选的URL。
    • 返回带有给定URL cookies的http.cookies.SimpleCookie。
  3. save(file_path)
    以pickle形式将cookies信息写入指定路径。

    file_path – 要写入的路径。字符串或pathlib.Path实例对象。

  4. load(file_path)
    从给定路径读取被pickle的cookies信息。

    file_path – 要导入的路径, 字符串或pathlib.Path实例对象。

DummyCookieJar类

1
class aiohttp.DummyCookieJar(*, loop=None)

假人cookie jar,用于忽略cookie。在一些情况下是很有用的: 比如写爬虫时不需要保存cookies信息,只要一直下载内容就好了。

将它的实例对象传入即可使用:

1
2
jar = aiohttp.DummyCookieJar()
session = aiohttp.ClientSession(cookie_jar=DummyCookieJar())

session.request方法

1
2
def session.request(method, url, *, params=None, data=None, json=None, headers=None, skip_auto_headers=None, auth=None, allow_redirects=True, max_redirects=10, compress=None, chunked=None, expect100=False, read_until_eof=True, proxy=None, proxy_auth=None, timeout=5*60, verify_ssl=None, fingerprint=None, ssl_context=None, proxy_headers=None):
pass
  • params :
    可传入Maaping对象,有键值对的元组或字符串,会在发送新请求时作为查询字符串发送。不会对之后的重定向请求也传入查询字符串

  • json:
    任何可被json解码的python对象。

    注: json不能与data参数同时使用。

  • skip_auto_headers :
    跳过会自动生成的headers信息。

  • allow_redirects(布尔类型) :
    如果设为False,则不允许重定向。默认是True。

  • compress (布尔类型) :
    默认是None。如果请求内容要进行deflate编码压缩可以设为True。

    如果设置了Content-Encoding和Content-Length则不要使用这个参数。

  • chunked (整数) :
    默认为None。允许使用分块编码传输。开发者想使用分块数据流时,用它就没错啦。

    如果是允许的,aiohttp将会设置为”Transfer-encoding:chunked”,这时不要在设置Transfer-encoding和content-length这两个headers了。

  • expect100(布尔类型) :
    默认是False。服务器返回100时将等待响应(返回100表示服务器正在处理数据)。

  • read_until_eof (布尔类型) :
    如果响应不含Content-Length头信息将会一直读取响应内容直到无内容可读。默认是True。

  • proxy_auth(aiohttp.BaicAuth) :
    传入表示HTTP代理基础认证的对象。

  • erify_ssl(布尔类型) :
    默认是True。对HTTPS请求验证SSL证书。

  • fringerprint (字节):
    传递想要验证的证书(使用DER编码的证书)的SHA256值来验证服务器是否可以成功匹配。对证书固定非常有用。

    警告: 不赞成使用不安全的MD5和SHA1哈希值。

  • proxy_headers(abc.Mapping) :
    如果该参数有提供内容,则会将其做为HTTP headers发送给代理。

session.get 和 session.post方法

1
2
3
4
5
def session.get(url, *, allow_redirects=True, **kwargs):
pass

def session.post(url, *, data=None, **kwargs):
pass

客户端

发起请求

1
2
3
4
5
6
7
8
import aiohttp

# ClientSession() 创建 会话(session)对象
async with aiohttp.ClientSession() as session:
# 会话对象发情get请求
async with session.get('https://api.github.com/events') as resp:
print(resp.status)
print(await resp.text())
1
2
print(type(session))   # <class 'aiohttp.client.ClientSession'>
print(type(resp)) # <class 'aiohttp.client_reqrep.ClientResponse'>

POST请求

1
2
3
4
5
6
7
session.post('http://httpbin.org/post', data=b'data')

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')

JSON请求

1
2
async with aiohttp.ClientSession() as session:
async with session.post(json={'test': 'object'})

传递URL中的请求参数

  • 使用字典
  • 使用元组
1
2
3
4
5
6
7
8
params = {'key1': 'value1', 'key2': 'value2'}
async with session.get('http://httpbin.org/get',params=params) as resp:
assert str(resp.url) == 'http://httpbin.org/get?key2=value2&key1=value1'

# 或者
params = [('key', 'value1'), ('key', 'value2')]
async with session.get('http://httpbin.org/get',params=params) as r:
assert str(r.url) == 'http://httpbin.org/get?key=value2&key=value1'

默认url会被编码

比如:URL('http://example.com/путь%30?a=%31') 会被转化为URL('http://example.com/%D0%BF%D1%83%D1%82%D1%8C/0?a=1')

如果想取消编码,使用encoded参数

1
await session.get(URL('http://example.com/%30', encoded=True))

警告:
传递params时不要用encode=True,这俩参数不能同时使用

获取响应内容

1
2
3
async with session.get('https://api.github.com/events') as resp:
print(await resp.text())
print(await resp.text(encoding='windows-1251')) # 修改编码

注意:

因为是异步请求,所以await resp.text()要使用await

但是resp.status不能使用await

获取二进制和json响应

  • 和requests一样,await resp.read()是获取二进制响应
  • 和requests一样,await resp.json()是获取json响应

获取流式响应

read(), json(), text()等方法会将所有的响应内容加载到内存

获取流式响应非常简单,只要使用content属性 : resp.content.read(10)即可

1
2
async with session.get('https://api.github.com/events') as resp:
await resp.content.read(10)

自定义Headers

和requests一样,使用header参数和data参数

1
2
3
4
5
6
import json
url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
headers = {'content-type': 'application/json'}

await session.post(url,data=json.dumps(payload),headers=headers)

自定义Cookies

ClientSession对象指定cookies参数:

使用ClientSession(cookies=cookies)将字典转为ClientSession对象,此时这个session自动就有了cookies值

1
2
3
4
5
url = 'http://httpbin.org/cookies'
cookies = {'cookies_are': 'working'}
async with ClientSession(cookies=cookies) as session:
async with session.get(url) as resp:
assert await resp.json() == {"cookies": {"cookies_are": "working"}}

获取当前的cookies

1
2
3
4
5
6
async with session.get(url) as resp:
print(resp.cookies)

url = 'http://example.com/some/cookie/setting/url'
async with session.get(url) as resp:
print(resp.cookies['example_cookie_name'])

响应中的cookies只包含重定向链中最后一个请求中的Set-Cookies头信息设置的值。
如果每一次重定向请求都收集一次cookies请使用 aiohttp.ClientSession对象.

发送多部分编码文件(Multipart-Encoded)

上传多部分编码文件:

1
2
3
4
url = 'http://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}

await session.post(url, data=files)

也可以显式地设置文件名,文件类型:

1
2
3
4
5
6
7
8
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 支持多种形式的流式上传,允许你直接发送大文件而不必读到内存

1
2
with open('massive-body', 'rb') as f:
await session.post('http://httpbin.org/post', data=f)

使用aiohttp.streamer对象:

1
2
3
4
5
6
7
8
9
10
11
@aiohttp.streamer
def file_sender(writer, file_name=None):
with open(file_name, 'rb') as f:
chunk = f.read(2**16)
while chunk:
yield from writer.write(chunk)
chunk = f.read(2**16)

# 之后你可以使用"file_sender"传递给data:
async with session.post('http://httpbin.org/post',data=file_sender(file_name='huge_file')) as resp:
print(await resp.text())

上传预压缩过的数据

上传一个已经压缩过的数据,需要为Headers中的Content-Encoding指定算法名(通常是deflate或者是zlib).

1
2
3
4
5
async def my_coroutine(session, headers, my_data):
data = zlib.compress(my_data)
headers = {'Content-Encoding': 'deflate'}
async with session.post('http://httpbin.org/post',data=data,headers=headers)
pass

持久连接(keep-alive), 连接池和cookies共享

ClientSession可以在多个请求之间共享cookies:

1
2
3
4
5
6
7
8
9
10
11
async with aiohttp.ClientSession() as session:
# 设置cookies
await session.get('http://httpbin.org/cookies/set?my_cookie=my_value')

# 筛选cookies
filtered = session.cookie_jar.filter_cookies('http://httpbin.org')
assert filtered['my_cookie'].value == 'my_value'

async with session.get('http://httpbin.org/cookies') as resp:
json_body = await resp.json()
assert json_body['cookies']['my_cookie'] == 'my_value'

你也可以为所有的会话请求设置headers:

1
2
3
4
5
async with aiohttp.ClientSession(headers={"Authorization": "Basic bG9naW46cGFzcw=="}) as session:

async with session.get("http://httpbin.org/headers") as resp:
json_body = await resp.json()
assert json_body['headers']['Authorization'] == 'Basic bG9naW46cGFzcw=='

ClientSession支持持久连接和连接池,可直接使用,不需要额外操作。

安全cookies

ClientSession中的默认的aiohttp.CookiesJar使用的是严苛模式,RFC 2109明确禁止使用ip地址形式的URL携带cookies信息。比如: http://127.0.0.1:80/cookie .
这样很好,不过有些时候我们测试时需要允许携带cookies。在aiohttp.CookiesJar中传递unsafe=True来实现这一效果:

1
2
jar = aiohttp.CookieJar(unsafe=True)
session = aiohttp.ClientSession(cookie_jar=jar)

使用虚假Cookie Jar

有时不想处理cookie。这时可以在会话中使用aiohttp.DummyCookieJar来达到目的。

1
2
jar = aiohttp.DummyCookieJar()
session = aiohttp.ClientSession(cookie_jar=jar)

使用连接器connector

想要调整请求的传输层你可以为ClientSession及其同类组件传递自定义的连接器。例如:

1
2
conn = aiohttp.TCPConnector()
session = aiohttp.ClientSession(connector=conn)

限制连接池的容量

限制同一时间打开的连接数可以传递limit参数:
所以可以用它来限制同时请求数量

1
2
conn = aiohttp.TCPConnector(limit=30)
session = aiohttp.ClientSession(connector=conn)
  • 默认情况下是100.
  • 为0则不作限制

限制同一时间在同一个端点((host, port, is_ssl) 3者都一样的情况)打开的连接数可指定limit_per_host参数:

1
conn = aiohttp.TCPConnector(limit_per_host=30)

使用自定义域名服务器

底层需要aiodns支持:

1
2
3
4
from aiohttp.resolver import AsyncResolver

resolver = AsyncResolver(nameservers=["8.8.8.8", "8.8.4.4"])
conn = aiohttp.TCPConnector(resolver=resolver)

为TCP sockets添加SSL控制

默认情况下aiohttp总会对使用了HTTPS协议(的URL请求)查验其身份。但也可将verify_ssl设置为False让其不检查:

1
r = await session.get('https://example.com', verify_ssl=False)

如果你需要设置自定义SSL信息(比如使用自己的证书文件)你可以创建一个ssl.SSLContext实例并传递到ClientSession中:

1
2
sslcontext = ssl.create_default_context(cafile='/path/to/ca-bundle.crt')
r = await session.get('https://example.com', ssl_context=sslcontext)

代理支持

aiohttp 支持 HTTP/HTTPS形式的代理。你需要使用proxy参数:

1
2
3
async with aiohttp.ClientSession() as session:
async with session.get("http://python.org",proxy="http://some.proxy.com") as resp:
print(resp.status)

同时支持认证代理:

1
2
3
4
5
6
async with aiohttp.ClientSession() as session:
proxy_auth = aiohttp.BasicAuth('user', 'pass')
async with session.get("http://python.org",
proxy="http://some.proxy.com",
proxy_auth=proxy_auth) as resp:
print(resp.status)
  • 与requests不同,aiohttp默认不会读取环境变量中的代理值。
  • 但你可以通过传递trust_env=True来让aiohttp.ClientSession读取HTTP_PROXY或HTTPS_PROXY环境变量中的代理信息(不区分大小写)。
1
2
3
async with aiohttp.ClientSession() as session:
async with session.get("http://python.org", trust_env=True) as resp:
print(resp.status)

还可以使用ProxyConnector对象:

1
2
3
4
conn = aiohttp.ProxyConnector(proxy="http://some.proxy.com")
session = aiohttp.ClientSession(connector=conn)
async with session.get('http://python.org') as resp:
print(resp.status)

查看响应状态码

1
2
async with session.get('http://httpbin.org/get') as resp:
assert resp.status == 200

设置Timeout

默认情况下每个IO操作有5分钟超时时间。可以通过给ClientSession.get()及其同类组件传递timeout来覆盖原超时时间

1
2
async with session.get('https://github.com', timeout=60) as r:
...

或者:

1
2
3
with aiohttp.Timeout(0.001):
async with aiohttp.get('https://github.com') as r:
await r.text()

None 或者0则表示不检测超时。 还可通过调用async_timeout.timeout上下文管理器来为连接和解析响应内容添加一个总超时时间:

1
2
3
4
5
import async_timeout

with async_timeout.timeout(0.001):
async with session.get('https://github.com') as r:
await r.text()

注意:
超时时间是累计的,包含如发送情况,重定向,响应解析,处理响应等所有操作在内…

获取响应头信息

ClientResponse.headers使用的数据类型是CIMultiDcitProxy:

1
2
3
4
5
6
7
8
resp.headers

# {'ACCESS-CONTROL-ALLOW-ORIGIN': '*',
# 'CONTENT-TYPE': 'application/json',
# 'DATE': 'Tue, 15 Jul 2014 16:49:51 GMT',
# 'SERVER': 'gunicorn/18.0',
# 'CONTENT-LENGTH': '331',
# 'CONNECTION': 'keep-alive'}

这是一个很特别的字典,他不分区大小写

1
2
3
4
5
resp.headers['Content-Type']
# 'application/json'

resp.headers.get('content-type')
# 'application/json'

获取响应历史

如果一个请求被重定向了,你可以用history属性查看其之前的响应:

1
2
3
4
5
resp = await session.get('http://example.com/some/redirect/')
# <ClientResponse(http://example.com/some/other/url/) [200]>

resp.history
# (<ClientResponse(http://example.com/some/redirect/) [301]>,)

如果没有重定向或allow_redirects设置为False,history会被设置为空。

使用WebSockets

  • aiohttp提供开箱即用的客户端websocket。
  • 使用aiohttp.ClientSession.ws_connect()协程对象。它的第一个参数接受URL,返回值是ClientWebSocketResponse,这样你就可以用响应的方法与websocket服务器进行通信。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
session = aiohttp.ClientSession()
# 连接目标网站的websocket
async with session.ws_connect('http://example.org/websocket') 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.CLOSED:
break
elif msg.type == aiohttp.WSMsgType.ERROR:
break

你只能使用一种读取方式 (例如await ws.receive() 或者 async for msg in ws:) 和写入方法,但可以有多个写入任务,写入任务也是异步完成的(ws.send_str('data'))。

  • 当一个包含ClientSessionasync with代码块的末尾行结束时(或直接调用了.close()),因为asyncio内部的一些原因底层的连接其实没有关闭

  • 在实际使用中,底层连接需要有一个缓冲时间来关闭。然而,如果事件循环在底层连接关闭之前就结束了,那么会抛出一个 资源警告:
    存在未关闭的传输(通道)(ResourceWarning: unclosed transport),如果警告可用的话。

  • 为了避免这种情况,在关闭事件循环前加入一小段延迟让底层连接得到关闭的缓冲时间。

    对于非SSL的ClientSession, 使用0即可(await asyncio.sleep(0)):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    async def read_website():
    async with aiohttp.ClientSession() as session:
    async with session.get('http://example.org/') as response:
    await response.read()

    loop = asyncio.get_event_loop()
    loop.run_until_complete(read_website())
    # 零睡眠以允许底层连接关闭
    loop.run_until_complete(asyncio.sleep(0))
    loop.close()

    对于使用了SSL的ClientSession, 需要设置一小段合适的时间:

    1
    2
    3
    # 等待250 ms以便关闭基础SSL连接
    loop.run_until_complete(asyncio.sleep(0.250))
    loop.close()

异常等级图

  • ClientError
    • ClientResponseError
      • ContentTypeError
      • WSServerHandshakeError
      • ClientHttpProxyError
    • ClientConnectionError
      • ClientOSError
        • ClientConnectorError
          • ClientSSLError
            • ClientConnectorCertificateError
            • ClientConnectorSSLError
          • ClientProxyConnectionError
        • ServerConnectionError
          • ServerDisconnectedError
          • ServerTimeoutError
        • ServerFingerprintMismatch
    • ClientPayloadError
    • InvalidURL