3.3 Aiohttp 的基本使用

虽然 requests 是一个非常优秀的请求库,但其并不支持异步编程,为了进一步提升爬虫工作的效率,这里就有了一个支持异步请求的 aiohttp 库,请安装 记得安装 Python3.5+ 版本。有了 aiohttp,爬虫运行效率会让你两眼一亮。

由于 aiohttp 库在许多方面的用法与 requests 类似,所以对于重复内容,这里只进行简单介绍。

1. 准备工作

开始之前请确保已正确安装好 aiohttp 库,如果没有安装可以参考先前的安装说明。

2. 实例引入

首先是导入aiohttp模块,然后我们试着获取一个web源码,这里以GitHub的公共Time-line页面为例:

import asyncio
import aiohttp

async def aiohttp_test01(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            print(resp.status)
            print(await resp.text())

loop = asyncio.get_event_loop()
tasks = [aiohttp_test01("https://api.github.com/events")]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

上面的代码中,我们创建了一个 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的操作方法也是一样的:

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

3. 在URL中传递参数

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

import aiohttp

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

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

import aiohttp

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

4. 响应的内容

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

import aiohttp

async with aiohttp.ClientSession() as session:
    async with session.get('https://api.github.com/events') as resp:
    print(await resp.text())
    print(await resp.text(encoding='gbk'))
    print(await resp.read())
    print(await resp.json())

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

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

5. 自定义请求头

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

import json
import aiohttp

url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
headers = {'content-type': 'application/json'}

async with aiohttp.ClientSession() as session:
    await session.post(url,
                       data=json.dumps(payload),
                       headers=headers)

6. 自定义Cookie

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

import aiohttp

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

7. post 数据

前面我们简单介绍了通过 post 方法进行简单的表单提交,这里我们再了解一下其他数据提交方式。

post json

import json
import aiohttp

url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}

async with aiohttp.ClientSession(cookies=cookies) as session:
    async with session.post(url, data=json.dumps(payload)) as resp:
    ...

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

post 小文件

import aiohttp

url = 'http://httpbin.org/post'
data = FormData()
data.add_field('file',
               open('report.xls', 'rb'),
               filename='report.xls',
               content_type='application/vnd.ms-excel')

async with aiohttp.ClientSession(cookies=cookies) as session:
    await session.post(url, data=data)

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

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

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

conn = aiohttp.TCPConnector(limit=30)

同时最大进行连接的连接数为30,默认是100,limit=0的时候是无限制。

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

conn = aiohttp.TCPConnector(limit_per_host=30)

9. 设置代理

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

import aiohttp

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

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

import aiohttp

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)

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

session.get("http://python.org", proxy="http://user:[email protected]")

10. 超时处理

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

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

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

results matching ""

    No results matching ""