zoukankan      html  css  js  c++  java
  • aiohttp基本及进阶使用

    客户端使用

    发起请求

    让我们从导入aiohttp模块开始:

    import aiohttp

    好啦,我们来尝试获取一个web页面。比如我们来获取下GitHub的时间轴。

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

    我们现在有了一个会话(session)对象,由ClientSession对象赋值而来,还有一个变量resp,它其实是ClientResponse对象。我们可以从这个响应对象中获取我们任何想要的信息。协程方法ClientSession.get()的主要参数接受一个HTTP URL。

    发起HTTP POST请求我们可以使用协程方法ClientSession.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')

    注意:

    不要为每个请求都创建一个会话。大多数情况下每个应用程序只需要一个会话就可以执行所有的请求。 每个会话对象都包含一个连接池,可复用的连接和持久连接状态(keep-alives,这两个是默认的)可提升总体的执行效率。

    发起JSON请求:

    每个会话的请求方法都可接受json参数。

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

    默认情况下会话(session)使用Python标准库里的json模块解析json信息。但还可使用其他的json解析器。可以给ClientSession指定json_serialize参数来实现:

    import ujson
    
    async with aiohttp.ClientSession(json_serialize=ujson.dumps) as session:
        async with session.post(json={'test': 'object'})

    传递URL中的参数:

    你可能经常想在URL中发送一系列的查询信息。如果你手动构建他们,这些信息会以键值对的形式出现在?后面,比如: httpbin.org/get?key=val。请求对象允许你使用dict(字典,python中的数据类型)发送它们,使用params参数即可。例如: 如果你要把 key1=value1,key2=value2放到httpbin.org/get后面,你可以用下面的方式:

    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'

    看,URL已经被正确的编码啦。 同键不同值的并联字典(MultiDict) 也同样支持。 可使用带有两个tuples(元组,python中的数据类型)的list(列表,python中的数据类型)来构建:

    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'

    同样也允许你传递str(字符串)给params,但要小心一些不能被编码的字符。就是一个不能被编码的字符:

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

    注意:

    aiohttp会在发送请求前标准化URL。 域名部分会用IDNA 编码,路径和查询条件会重新编译(requoting)。 比如:URL('http://example.com/путь%30?a=%31')会被转化为URL('http://example.com/%D0%BF%D1%83%D1%82%D1%8C/0?a=1') 如果服务器需要接受准确的表示并不要求编译URL,那标准化过程应是禁止的。 禁止标准化可以使用encoded=True:

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

    警告:

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

    获取响应内容

    我们可以读取服务器的响应内容。想想我们获取GitHub时间轴的例子:

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

    这样会打印出类似于下面的信息:

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

    aiohttp将会自动解码内容。你可以为text()方法指定编码(使用encoding参数):

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

    获取二进制响应内容

    你也可以以字节形式获取响应,这样得到的就不是文本了:

    print(await resp.read())
    b'[{"created_at":"2015-06-12T14:06:22Z","public":true,"actor":{...

    gzipdefalte传输编码会自动解码。 你也可以使其支持brotli传输编码的解码,只需安装brotlipy即可。

    获取JSON响应内容

    以防你需要处理JSON数据,内置了一个JSON解码器:

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

    如果JSON解码失败,json()方法将会抛出一个异常。你还可以在调用json()时指定编码器和解码器函数。

    注意:

    这些方法会读出内存中所有响应的内容。如果你要读非常多的数据,考虑使用流式响应方法进行读取。请看之后的文档。

    获取流式响应内容

    read(), json(), text()等方法使用起来很方便,但也要注意谨慎地使用。上述方法会将所有的响应内容加载到内存。举个例子,如果你要下载几个G的文件,这些方法还是会将所有内容都加载到内存,内存会表示"臣妾做不到啊~"(如果内存不够的话)。作为代替你可以用content属性。content其实是 aiohttp.StreamReader类的实例。gzipdeflate传输编码同样会自动解码。

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

    一般情况下你可以使用下列模式将内容保存在一个文件中:

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

    在使用content读了数据后,就不要在用read(), json(), text()了。

    获取请求信息

    ClientResponse(客户端响应)对象含有request_info(请求信息),主要是urlheaders信息。 raise_for_status结构体上的信息会被复制给ClientResponseError实例。

    自定义Headers

    如果你需要给某个请求添加HTTP头,可以使用headers参数,传递一个dict对象即可。 比如,如果你想给之前的例子指定 content-type可以这样:

    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

    发送你自己的cookies给服务器,你可以为ClientSession对象指定cookies参数:

    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"}}

    注意:

    访问httpbin.org/cookies 会看到以JSON形式返回的cookies。查阅会话中的cookies请看ClientSession.cookie_jar。

    发起更复杂的POST请求

    一般来说,如果你想以表单形式发送一些数据 - 就像HTML表单。那么只需要简单的将一个dict通过data参数传递就可以。传递的dict数据会自动编码:

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

    如果你想发送非表单形式的数据你可用str(字符串)代替dict(字典)。这些数据会直接发送出去。 例如,GitHub API v3 接受JSON编码POST/PATCH数据:

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

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

    上传多部分编码文件:

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

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

    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会自动将其以流的形式上传。查看StreamReader以获取支持的格式信息。

    流式上传

    aiohttp 支持多种形式的流式上传,允许你直接发送大文件而不必读到内存。

    下面是个简单的例子,提供类文件对象即可:

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

    或者你也可以使用aiohttp.streamer对象:

    @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())

    同样可以使用StreamReader对象.

    我们来看下如何把来自于另一个请求的内容作为文件上传并计算其SHA1值:

    async def feed_stream(resp, stream):
        h = hashlib.sha256()
    
        while True:
            chunk = await resp.content.readany()
            if not chunk:
                break
            h.update(chunk)
            stream.feed_data(chunk)
    
        return h.hexdigest()
    
    resp = session.get('http://httpbin.org/post')
    stream = StreamReader()
    loop.create_task(session.post('http://httpbin.org/post', data=stream))
    
    file_hash = await feed_stream(resp, stream)

    因为响应对象的content属性是一个StreamReader实例,所以你可以将get和post请求连在一起用:

    r = await session.get('http://python.org')
    await session.post('http://httpbin.org/post',
                       data=r.content)
  • 相关阅读:
    iOS开发学习树
    iOS开发数据库篇—FMDB数据库队列
    iOS开发数据库篇—FMDB简单介绍
    iOS开发数据库篇—SQLite常用的函数
    iOS开发数据库篇—SQLite模糊查询
    iOS开发数据库篇—SQLite的应用
    iOS开发数据库篇—SQL代码应用示例
    iOS开发数据库篇—SQL使用方法
    iOS开发数据库篇—SQLite简单介绍
    C#-汉字转拼音缩写
  • 原文地址:https://www.cnblogs.com/zycorn/p/10554206.html
Copyright © 2011-2022 走看看