• 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  ...'
    
  • 相关阅读:
    LeetCode(287)Find the Duplicate Number
    LeetCode(290) Word Pattern
    LeetCode(205)Isomorphic Strings
    LeetCode(201) Bitwise AND of Numbers Range
    LeetCode(200) Number of Islands
    LeetCode(220) Contains Duplicate III
    LeetCode(219) Contains Duplicate II
    命令行执行Qt程序
    LeetCode(228) Summary Ranges
    redis 的安装和使用记录
  • 原文地址:https://www.cnblogs.com/yoyoketang/p/16168130.html
Copyright © 2020-2023  润新知