• aiohttp 异步http请求1.快速入门 get 请求示例 上海


    前言

    在 python 的众多 http 请求库中,大家最熟悉的就是 requests 库了,requests 库上手非常容易,适合入门学习。
    如果平常工作中对发请求不追求效率和并发的情况下,requests 完全可以满足大部分需求。但是想发大量的请求,比如几万个请求的时候,可能需要等待几个小时,requests 库就不能满足需求了。
    初学者建议先学习requests 库,熟练掌握requests 库才能更好的学习 aiohttp 异步框架。

    同步与异步

    requests只能发送同步请求,aiohttp只能发送异步请求。
    所谓的同步请求,是指在单进程单线程的代码中,发起一次请求后,在收到返回结果之前,不能发起下一次请求。
    所谓异步请求,是指在单进程单线程的代码中,发起一次请求后,在等待网站返回结果的时间里,可以继续发送更多请求。

    在前面一篇中有讲到python asyncio 异步 I/O - 实现并发http请求(asyncio + aiohttp)
    如果使用requests 库,发10个请求访问我的博客,那么这10个请求是串行的。

    import requests
    import time
    
    url = "https://www.cnblogs.com/yoyoketang/"
    
    start_time = time.time()
    for i in range(10):
        r = requests.get(url)
        print(r)
    print('总耗时:', time.time()-start_time)
    

    我们想实现并发请求需用到 异步http 库 aiohttp。

    环境准备

    首先,确保 aiohttp 已安装,为了更好的学习 aiohttp 的功能,建议大家使用python3.7+版本, 我用的是python3.8版本

    pip install aiohttp==3.8.1
    

    或者在 pycharm 中安装

    简单get 请求实现

    首先导入 aiohttp 模块和 asyncio

    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())
    
    
    # asyncio.run(main()) # 会报错,改成下面2句
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    

    运行结果

    200
    {
      "args": {}, 
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Host": "httpbin.org", 
        "User-Agent": "Python/3.8 aiohttp/3.8.1", 
        "X-Amzn-Trace-Id": "Root=1-625ed4ea-5f44f6163dc3521845687df1"
      }, 
      "origin": "183.193.27.228", 
      "url": "http://httpbin.org/get"
    }
    

    上面代码解释:

    • aiohttp.ClientSession() 是创建客户端session会话对象
    • resp 是返回的response对象
    • session.get 发get请求
    • resp.status 获取返回对象状态码
    • resp.text() 返回文本对象

    前面是get请求示例,发一个 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')
    

    为了使对同一个站点的多个请求更简单,可以使用构造函数的参数base_url ,例如请求不同的端点 http://httpbin.org 可以使用以下代码:

    import aiohttp
    import asyncio
    
    
    async def main():
        async with aiohttp.ClientSession('http://httpbin.org') as session:
            async with session.get('/get') as resp1:
                print(resp1.status)
            async with session.post('/post', data=b'data') as resp2:
                print(resp2.status)
            async with session.put('/put', data=b'data') as resp3:
                print(resp3.status)
    
    # asyncio.run(main()) # 会报错,改成下面2句
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    

    笔记

    不要为每个请求创建会话。尽可能在每个应用程序中使用一个会话执行所有的请求。
    更复杂的情况可能需要每个站点一个会话,例如一个用于 Github,另一个用于 Facebook API。无论如何,为每个请求创建一个会话是一个非常糟糕的主意。
    会话内部包含一个连接池。连接重用和保持活动(默认情况下都打开)可以提高整体性能。

    会话上下文管理器的使用不是强制性的,但在这种情况下应该调用方法,例如:await session.close()

    session = aiohttp.ClientSession()
    async with session.get('...'):
        # ...
    await session.close()
    

    在 URL 中传递参数

    当url中带请求参数时,如http://httpbin.org/get?key1=value1&key2=value2,在url中问号后面的参数可以单独拿出来用键值对保存,使用 params关键字参数将这些参数作为 提供

    import aiohttp
    import asyncio
    
    
    async def main():
        async with aiohttp.ClientSession() as session:
            params = {'key1': 'value1', 'key2': 'value2'}
            async with session.get('http://httpbin.org/get', params=params) as resp:
                expect = 'http://httpbin.org/get?key1=value1&key2=value2'
                print(resp.url)
                assert str(resp.url) == expect
    
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    

    运行打印结果:http://httpbin.org/get?key1=value1&key2=value2,通过打印 URL,您可以看到 URL 已正确编码。

    如果同一个key有多个值的时候,如:http://httpbin.org/get?key1=value1&key2=value2&key2=value3,其中key2对应的值有2个value2和value3。
    那么在传值的时候,可以把多个值放到一个list,如

    params = {
                'key1': 'value1',
                'key2': ['value2', 'value3']
            }
    

    也可以使用列表嵌套元祖

    params = [
                ('key1', 'value1'),
                ('key2', 'value2'),
                ('key2', 'value3')
            ]
    

    也可以把字符串内容作为参数传递, 如params='key=value+1&key2=value2'.

            async with session.get('http://httpbin.org/get',
                                   params='key=value+1&key2=value2') as r:
                print(r.url)
                assert str(r.url) == 'http://httpbin.org/get?key=value+1&key2=value2'
    

    但是这样会有弊端,+号这种特殊字符没转码成urlencoded格式%2B,所以这种方式尽量不用,以下是官方文档把字符串内容作为参数传递的说明。

    也可以把参数直接传到url上,如http://httpbin.org/get?key1=value1&key2=value2

    import aiohttp
    import asyncio
    
    
    async def main():
        async with aiohttp.ClientSession() as session:
            async with session.get('http://httpbin.org/get?key1=value1&key2=value2') as resp:
                print(resp.url)
    
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    

    当请求参数带有中文的时候

    params = {
                'key1': 'value1',
                'key2': '上海-悠悠'
            }
    

    它会自动帮我们转成urlencode编码http://httpbin.org/get?key1=value1&key2=%E4%B8%8A%E6%B5%B7-%E6%82%A0%E6%82%A0

    async def main():
        async with aiohttp.ClientSession() as session:
            params = {
                'key1': 'value1',
                'key2': '上海-悠悠'
            }
            async with session.get('http://httpbin.org/get', params=params) as resp:
                print(resp.url)
    

    响应内容和状态码

    我们可以读取服务器响应的内容及其状态码

    import aiohttp
    import asyncio
    
    
    async def main():
        async with aiohttp.ClientSession() as session:
            async with session.get('https://www.cnblogs.com/yoyoketang/') as resp:
                print(resp.url)
                print(await resp.text(encoding='utf-8'))
       
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    

    打印出内容:

    https://www.cnblogs.com/yoyoketang/
    <!DOCTYPE html>
    <html lang="zh-cn">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta name="referrer" content="origin-when-crossorigin" />
        
        
        <meta http-equiv="Cache-Control" content="no-transform" />
        <meta http-equiv="Cache-Control" content="no-siteapp" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <title>上海-悠悠 - 博客园</title>
    

    aiohttp自动解码来自服务器的内容。您可以为该text()方法指定自定义编码:

    resp.text(encoding='utf-8')
    

    也可以获取byte响应内容

    print(await resp.read())
    

    打印结果

    b'<!DOCTYPE html>\n<html lang="zh-cn">\n<head>\n    <meta charset="utf-8" />\n  ...'
    
  • 相关阅读:
    python实现双向链表
    django contenttypes
    tensorflow学习笔记一
    vue指令和事件绑定
    es6简单介绍
    mysql主从复制
    mysql事务
    winform 使用 ReportViewer做报表
    设置控件获取焦点
    修改安卓串口蓝牙app问题记录
  • 原文地址:https://www.cnblogs.com/yoyoketang/p/16168130.html
Copyright © 2020-2023  润新知