aiohttp简单使用教程

aiohttp简单使用教程0 前言本文翻译自 aiohttp 的官方文档 如有纰漏 欢迎指出 aiohttp 分为服务器端和客户端 本文只介绍客户端 由于上下文的缘故 请求代码必须在一个异步的函数中进行 asyncdeffn pass1 aiohttp 安装 pipinstallai 1 基本请求用法 asyncwithaio request GET

0. 前言

本文翻译自aiohttp的官方文档,如有纰漏,欢迎指出。

aiohttp分为服务器端和客户端,本文只介绍客户端。

由于上下文的缘故,请求代码必须在一个异步的函数中进行:

async def fn():

pass

1. aiohttp安装

pip install aiohttp

1.1. 基本请求用法

  1. async with aiohttp.request('GET','https://github.com') as r:
  2. await r.text()

其中r.text(), 可以在括号中指定解码方式,编码方式,例如

await resp.text(encoding='windows-1251')

 

或者也可以选择不编码,适合读取图像等,是无法编码的

await resp.read()

 

  1. #使用示例, 进行一次请求
  2.  
  3. import aiohttp, asyncio
  4.  
  5. async def main():#aiohttp必须放在异步函数中使用
  6. async with aiohttp.request('GET', 'https://api.github.com/events') as resp:
  7. json = await resp.json()
  8. print(json)
  9.  
  10. loop = asyncio.get_event_loop()
  11. loop.run_until_complete(main())
  12.  
  13. ------------------------------------------------------------------------------
  14. #使用示例,进行多次请求
  15.  
  16. import aiohttp, asyncio
  17.  
  18. async def main():#aiohttp必须放在异步函数中使用
  19. tasks = []
  20. [tasks.append(fetch('https://api.github.com/events?a={}'.format(i))) for i in range(10)]#十次请求
  21. await asyncio.wait(tasks)
  22.  
  23. async def fetch(url):
  24. async with aiohttp.request('GET', url) as resp:
  25. json = await resp.json()
  26. print(json)
  27.  
  28. loop = asyncio.get_event_loop()
  29. loop.run_until_complete(main())
  30. ------------------------------------------------------------------------------
  31. #使用示例,进行多次请求,并限制同时请求的数量
  32.  
  33. import aiohttp, asyncio
  34.  
  35. async def main(pool):#aiohttp必须放在异步函数中使用
  36. tasks = []
  37. sem = asyncio.Semaphore(pool)#限制同时请求的数量
  38. [tasks.append(control_sem(sem, 'https://api.github.com/events?a={}'.format(i))) for i in range(10)]#十次请求
  39. await asyncio.wait(tasks)
  40.  
  41. async def control_sem(sem, url):#限制信号量
  42. async with sem:
  43. await fetch(url)
  44.  
  45. async def fetch(url):
  46. async with aiohttp.request('GET', url) as resp:
  47. json = await resp.json()
  48. print(json)
  49.  
  50. loop = asyncio.get_event_loop()
  51. loop.run_until_complete(main(pool=2))

上面的示例中可以正确的使用协程进行请求,但是由于aiohttp自身的原因会报 Unclosed client session 的警告。官方不推荐使用aiohttp.request的方式请求,可以将 aiohttp.request 换成 aiohttp.ClientSession(kw).request的方式即可。 

具体请看2.发起一个session请求

2.发起一个session请求

首先是导入aiohttp模块:

import aiohttp

然后我们试着获取一个web源码,这里以GitHub的public Time-line页面为例:

  1. async with aiohttp.ClientSession() as session:
  2. async with session.get('https://api.github.com/events') as resp:
  3. print(resp.status)
  4. print(await resp.text())

上面的代码中,我们创建了一个 ClientSession 对象命名为session,然后通过session的get方法得到一个 ClientResponse 对象,命名为resp,get方法中传入了一个必须的参数url,就是要获得源码的http url。至此便通过协程完成了一个异步IO的get请求。

有get请求当然有post请求,并且post请求也是一个协程:

session.post('http://httpbin.org/post', data=b'data')

用法和get是一样的,区别是post需要一个额外的参数data,即是需要post的数据。

除了get和post请求外,其他http的操作方法也是一样的:

  1. session.put('http://httpbin.org/put', data=b'data')
  2. session.delete('http://httpbin.org/delete')
  3. session.head('http://httpbin.org/get')
  4. session.options('http://httpbin.org/get')
  5. session.patch('http://httpbin.org/patch', data=b'data')

小记:

不要为每次的连接都创建一次session,一般情况下只需要创建一个session,然后使用这个session执行所有的请求。

每个session对象,内部包含了一个连接池,并且将会保持连接和连接复用(默认开启)可以加快整体的性能。

  1. #使用示例
  2.  
  3. import aiohttp, asyncio
  4.  
  5. async def main(pool):#启动
  6. sem = asyncio.Semaphore(pool)
  7. async with aiohttp.ClientSession() as session:#给所有的请求,创建同一个session
  8. tasks = []
  9. [tasks.append(control_sem(sem, 'https://api.github.com/events?a={}'.format(i), session)) for i in range(10)]#十次请求
  10. await asyncio.wait(tasks)
  11.  
  12. async def control_sem(sem, url, session):#限制信号量
  13. async with sem:
  14. await fetch(url, session)
  15.  
  16. async def fetch(url, session):#开启异步请求
  17. async with session.get(url) as resp:
  18. json = await resp.json()
  19. print(json)
  20.  
  21. loop = asyncio.get_event_loop()
  22. loop.run_until_complete(main(pool=2))

速度快的不要不要的

3.在URL中传递参数

我们经常需要通过 get 在url中传递一些参数,参数将会作为url问号后面的一部分发给服务器。在aiohttp的请求中,允许以dict的形式来表示问号后的参数。举个例子,如果你想传递 key1=value1   key2=value2 到 httpbin.org/get 你可以使用下面的代码:

  1. params = {'key1': 'value1', 'key2': 'value2'}
  2. async with session.get('http://httpbin.org/get',
  3. params=params) as resp:
  4. assert resp.url == 'http://httpbin.org/get?key2=value2&key1=value1'

可以看到,代码正确的执行了,说明参数被正确的传递了进去。不管是一个参数两个参数,还是更多的参数,都可以通过这种方式来传递。除了这种方式之外,还有另外一个,使用一个 list 来传递(这种方式可以传递一些特殊的参数,例如下面两个key是相等的也可以正确传递):

  1. params = [('key', 'value1'), ('key', 'value2')]
  2. async with session.get('http://httpbin.org/get',
  3. params=params) as r:
  4. assert r.url == 'http://httpbin.org/get?key=value2&key=value1'

除了上面两种,我们也可以直接通过传递字符串作为参数来传递,但是需要注意,通过字符串传递的特殊字符不会被编码:

  1. async with session.get('http://httpbin.org/get',
  2. params='key=value+1') as r:
  3. assert r.url == 'http://httpbin.org/get?key=value+1'

4.响应的内容

还是以GitHub的公共Time-line页面为例,我们可以获得页面响应的内容:

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

运行之后,会打印出类似于如下的内容:

'[{"created_at":"2015-06-12T14:06:22Z","public":true,"actor":{...

resp的text方法,会自动将服务器端返回的内容进行解码–decode,当然我们也可以自定义编码方式:

await resp.text(encoding='gb2312')

除了text方法可以返回解码后的内容外,我们也可以得到类型是字节的内容:

print(await resp.read())

运行的结果是:

b'[{"created_at":"2015-06-12T14:06:22Z","public":true,"actor":{...

 

gzip和deflate转换编码已经为你自动解码。

小记:

text(),read()方法是把整个响应体读入内存,如果你是获取大量的数据,请考虑使用”字节流“(streaming response)

5.特殊响应内容:json

如果我们获取的页面的响应内容是json,aiohttp内置了更好的方法来处理json:

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

如果因为某种原因而导致resp.json()解析json失败,例如返回不是json字符串等等,那么resp.json()将抛出一个错误,也可以给json()方法指定一个解码方式:

print(await resp.json(
encoding='gb2312'

)) 或者传递一个函数进去:

print(await resp.json( lambda(x:x.replace('a','b')) ))

 

6.以字节流的方式读取响应内容

虽然json(),text(),read()很方便的能把响应的数据读入到内存,但是我们仍然应该谨慎的使用它们,因为它们是把整个的响应体全部读入了内存。即使你只是想下载几个字节大小的文件,但这些方法却将在内存中加载所有的数据。所以我们可以通过控制字节数来控制读入内存的响应内容:

  1. async with session.get('https://api.github.com/events') as resp:
  2. await resp.content.read(10) #读取前10个字节

一般地,我们应该使用以下的模式来把读取的字节流保存到文件中:

  1. with open(filename, 'wb') as fd:
  2. while True:
  3. chunk = await resp.content.read(chunk_size)
  4. if not chunk:
  5. break
  6. fd.write(chunk)

7.自定义请求头

如果你想添加请求头,可以像get添加参数那样以dict的形式,作为get或者post的参数进行请求:

  1. import json
  2. url = 'https://api.github.com/some/endpoint'
  3. payload = {'some': 'data'}
  4. headers = {'content-type': 'application/json'}
  5.  
  6. await session.post(url,
  7. data=json.dumps(payload),
  8. headers=headers)

8.自定义Cookie

给服务器发送cookie,可以通过给 ClientSession 传递一个cookie参数:

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

可直接访问链接 “httpbin.org/cookies”查看当前cookie,访问session中的cookie请见第10节。

9.post数据的几种方式

(1)模拟表单post数据

  1. payload = {'key1': 'value1', 'key2': 'value2'}
  2. async with session.post('http://httpbin.org/post',
  3. data=payload) as resp:
  4. print(await resp.text())

注意:data=dict的方式post的数据将被转码,和form提交数据是一样的作用,如果你不想被转码,可以直接以字符串的形式 data=str 提交,这样就不会被转码。

(2)post json

  1. import json
  2. url = 'https://api.github.com/some/endpoint'
  3. payload = {'some': 'data'}
  4.  
  5. async with session.post(url, data=json.dumps(payload)) as resp:
  6. ...

其实json.dumps(payload)返回的也是一个字符串,只不过这个字符串可以被识别为json格式

(3)post 小文件

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

可以设置好文件名和content-type:

  1. url = 'http://httpbin.org/post'
  2. data = FormData()
  3. data.add_field('file',
  4. open('report.xls', 'rb'),
  5. filename='report.xls',
  6. content_type='application/vnd.ms-excel')
  7.  
  8. await session.post(url, data=data)

如果将文件对象设置为数据参数,aiohttp将自动以字节流的形式发送给服务器。

(4)post 大文件

aiohttp支持多种类型的文件以流媒体的形式上传,所以我们可以在文件未读入内存的情况下发送大文件。

  1. @aiohttp.streamer
  2. def file_sender(writer, file_name=None):
  3. with open(file_name, 'rb') as f:
  4. chunk = f.read(216)
  5. while chunk:
  6. yield from writer.write(chunk)
  7. chunk = f.read(216)
  8.  
  9. # Then you can use `file_sender` as a data provider:
  10.  
  11. async with session.post('http://httpbin.org/post',
  12. data=file_sender(file_name='huge_file')) as resp:
  13. print(await resp.text())

同时我们可以从一个url获取文件后,直接post给另一个url,并计算hash值:

  1. async def feed_stream(resp, stream):
  2. h = hashlib.sha256()
  3.  
  4. while True:
  5. chunk = await resp.content.readany()
  6. if not chunk:
  7. break
  8. h.update(chunk)
  9. stream.feed_data(chunk)
  10.  
  11. return h.hexdigest()
  12.  
  13. resp = session.get('http://httpbin.org/post')
  14. stream = StreamReader()
  15. loop.create_task(session.post('http://httpbin.org/post', data=stream))
  16.  
  17. file_hash = await feed_stream(resp, stream)

因为响应内容类型是StreamReader,所以可以把get和post连接起来,同时进行post和get:

  1. r = await session.get('http://python.org')
  2. await session.post('http://httpbin.org/post',
  3. data=r.content)

(5)post预压缩数据

在通过aiohttp发送前就已经压缩的数据, 调用压缩函数的函数名(通常是deflate 或 zlib)作为content-encoding的值:

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

10.keep-alive, 连接池,共享cookie

ClientSession 用于在多个连接之间共享cookie:

  1. async with aiohttp.ClientSession() as session:
  2. await session.get(
  3. 'http://httpbin.org/cookies/set?my_cookie=my_value')
  4. filtered = session.cookie_jar.filter_cookies('http://httpbin.org')
  5. assert filtered['my_cookie'].value == 'my_value'
  6. async with session.get('http://httpbin.org/cookies') as r:
  7. json_body = await r.json()
  8. assert json_body['cookies']['my_cookie'] == 'my_value'

也可以为所有的连接设置共同的请求头:

  1. async with aiohttp.ClientSession(
  2. headers={"Authorization": "Basic bG9naW46cGFzcw=="}) as session:
  3. async with session.get("http://httpbin.org/headers") as r:
  4. json_body = await r.json()
  5. assert json_body['headers']['Authorization'] == \
  6. 'Basic bG9naW46cGFzcw=='

ClientSession 还支持 keep-alive连接和连接池(connection pooling)

11.cookie安全性

默认ClientSession使用的是严格模式的 aiohttp.CookieJar. RFC 2109,明确的禁止接受url和ip地址产生的cookie,只能接受 DNS 解析IP产生的cookie。可以通过设置aiohttp.CookieJar 的 unsafe=True 来配置:

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

12.控制同时连接的数量(连接池)

也可以理解为同时请求的数量,为了限制同时打开的连接数量,我们可以将限制参数传递给连接器:

conn = aiohttp.TCPConnector(limit=30)#同时最大进行连接的连接数为30,默认是100,limit=0的时候是无限制

限制同时打开限制同时打开连接到同一端点的数量((host, port, is_ssl) 三的倍数),可以通过设置 limit_per_host 参数:

conn = aiohttp.TCPConnector(limit_per_host=30)#默认是0

 

13.自定义域名解析

我们可以指定域名服务器的 IP 对我们提供的get或post的url进行解析:

  1. from aiohttp.resolver import AsyncResolver
  2.  
  3. resolver = AsyncResolver(nameservers=["8.8.8.8", "8.8.4.4"])
  4. conn = aiohttp.TCPConnector(resolver=resolver)

14.设置代理

aiohttp支持使用代理来访问网页:

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

当然也支持需要授权的页面:

  1. async with aiohttp.ClientSession() as session:
  2. proxy_auth = aiohttp.BasicAuth('user', 'pass')
  3. async with session.get("http://python.org",
  4. proxy="http://some.proxy.com",
  5. proxy_auth=proxy_auth) as resp:
  6. print(resp.status)

或者通过这种方式来验证授权:

  1. session.get("http://python.org",
  2. proxy="http://user:")

15.响应状态码 response status code

可以通过 resp.status来检查状态码是不是200:

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

16.响应头

我们可以直接使用 resp.headers 来查看响应头,得到的值类型是一个dict:

  1. >>> resp.headers
  2. {'ACCESS-CONTROL-ALLOW-ORIGIN': '*',
  3. 'CONTENT-TYPE': 'application/json',
  4. 'DATE': 'Tue, 15 Jul 2014 16:49:51 GMT',
  5. 'SERVER': 'gunicorn/18.0',
  6. 'CONTENT-LENGTH': '331',
  7. 'CONNECTION': 'keep-alive'}

或者我们可以查看原生的响应头:

  1. >>> resp.raw_headers
  2. ((b'SERVER', b'nginx'),
  3. (b'DATE', b'Sat, 09 Jan 2016 20:28:40 GMT'),
  4. (b'CONTENT-TYPE', b'text/html; charset=utf-8'),
  5. (b'CONTENT-LENGTH', b'12150'),
  6. (b'CONNECTION', b'keep-alive'))

17.查看cookie

  1. url = 'http://example.com/some/cookie/setting/url'
  2. async with session.get(url) as resp:
  3. print(resp.cookies)

18.重定向的响应头

如果一个请求被重定向了,我们依然可以查看被重定向之前的响应头信息:

  1. >>> resp = await session.get('http://example.com/some/redirect/')
  2. >>> resp

  3. >>> resp.history
  4. (

    ,)

19.超时处理

默认的IO操作都有5分钟的响应时间 我们可以通过 timeout 进行重写:

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

如果 timeout=None 或者 timeout=0 将不进行超时检查,也就是不限时长。

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/227704.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午8:44
下一篇 2026年3月16日 下午8:44


相关推荐

  • python获取当前时间戳和日期

    python获取当前时间戳和日期importtime datetime 时间戳 print time time 今天的日期 print datetime date today 转载来源 https blog csdn net weixin article details

    2026年3月19日
    3
  • DB9 公头母头引脚定义及连接

    DB9 公头母头引脚定义及连接

    2021年11月16日
    55
  • windows下安装python及第三方库numpy、scipy、matplotlib终极版[通俗易懂]

    一、python安装进入python官网https://www.python.org/,点击Downloads–Windows下载对应的python2.7或者3.6。点击其中一个版本进入下一个界面,选择64Windowsx86-64MSIinstaller或者32位Windowsx86MSIinstaller进行下载。下载点击安装,默认安装位置是C盘根目录C…

    2022年4月11日
    158
  • 高级游标

    高级游标高级游标参数化游标 declareCURSO zip p stateINzipco state TYPE ISSELECTzip city stateFROMZIP p state BEGINforv zipinc zip NJ loop endloop END 复杂的嵌套游标

    2026年3月16日
    2
  • 智能点餐系统开发纪实2—–系统整体结构和业务流程「建议收藏」

    1请看这里http://blog.csdn.net/jason0539/article/details/20854329 写完之前这个就没写,趁着新鲜感几天就把这个弄完了,也没再写别的东西,前几天已经完成了,今天需要写一个文档,写完文档就把里面我写的部分贴到这里来了,有点罗嗦,凑字数。其实就是讲了一个整体流程,整个系统主要分四部分:小车,手机,收银台,厨房。服务器写了两个,收银

    2022年3月11日
    45
  • 这篇不讨好任何人的回忆录,记录了我从双非学校到BAT/TMD六offer的原因

    这篇不讨好任何人的回忆录,记录了我从双非学校到BAT/TMD六offer的原因注:给我想个新名字好不好呀,采用了直接发百元红包!没别的,想让大家认识兔兔rabbit,说一下自己的经验教训,应该会对很多人有帮助。一、前言在今年,我要毕业了,基本结束了大学生活,踏入了工作环境,觉得是时候写一个总结,给这段时光一个交代,也让多年后的我,还能回忆起这段经历,不忘初心。想起小学作文有个结构叫“总分总”,那我就先来简单的总结一下这两个环境吧。二、我的学校和工作学校,说实话,出了省就很少人知道的学校,学生基本和一流互联网公司无缘,如果范围缩小到后端开发,我觉得可以把基本去掉.

    2025年11月22日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号