Python-aiohttp-异步web服务-爬虫

本文最后更新于:2020年12月24日 上午

简介

requests是一个阻塞式HTTP请求库,发出一个请求后,程序会一直等待服务器响应,得到响应后,程序才会进行下一步处理。这种等待会大大降低爬取效率
aiohttp是一个提供异步web服务的库,能让你的程序可以在等待的过程中做一切其他的事情,提高效率

文档信息

官方文档:https://docs.aiohttp.org/en/stable/
github:https://github.com/aio-libs/aiohttp

安装

1
pip install aiohttp

另外,官方推荐安装 字符编码检测库 cchardet,加速DNS解析库aiodns

1
pip install cchardet aiodns

入门

基础

1
2
3
4
5
6
7
8
9
10
11
# 首先,你要引入 aiohttp
import aiohttp

print('Hello Aiohttp')

# 创建一个名为 session 的 ClientSession 对象
async with aiohttp.ClientSession() as session:
# ClientSession 对象发起请求,获取结果保存为 resp
async with session.get('http://httpbin.org/get') as resp:
print(resp.status) # 响应状态码
print(await resp.text()) # 响应内容

这段代码创建了一个session以用作访问,但最好不要每一次访问都创建session,如果可以的话同一个网站用同一个session来进行访问

实际上除了get()还有一些其它的方法

1
2
3
4
session.post('http://httpbin.org/post', data=b'data')
session.put('http://httpbin.org/put', data=b'data')
session.delete('http://httpbin.org/delete')
......

带参数的get请求

传入params参数即可

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

关于url转换

aiohttp默认会在发送请求之前会在内部执行URL规范化
比如将url中的中文转为其它编码
如果不希望进行这个过程,可以传入encoded=True禁用

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

需要注意的是,如果你get方法中传入了params参数,那么这个禁用会无效

二进制响应

如果响应是二进制文件,那么需要用read()来获取

1
await resp.read()

Json请求

session的任何方法都可以传入json参数

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

session会使用python自带的json模块来进行序列化
你也可以使用比它更快的ujson模块来进行这个过程

1
2
3
4
5
import ujson

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

Json响应

resp.json()可以直接解析json

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

响应内容流

read()json()text()方法非常方便,但应该要谨慎使用。这些方法会将整个响应加载到内存中。要是响应很大,就会造成问题

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

你可以一些一些的去处理响应

post请求

传入表单参数即可,编码会自动完成

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

如果只希望传递字符,可以用data参数

1
2
async with session.post(url, data='Тест') as resp:
...

上传文件

一点点载入,一点点上传

1
2
3
4
5
6
7
8
9
10
11
12
async def file_sender(file_name=None):
async with aiofiles.open(file_name, 'rb') as f:
chunk = await f.read(64*1024)
while chunk:
yield chunk
chunk = await f.read(64*1024)

# Then you can use file_sender as a data provider:

async with session.post('http://httpbin.org/post',
data=file_sender(file_name='huge_file')) as resp:
print(await resp.text())

WebSockets

1
2
3
4
5
6
7
8
9
10
async with session.ws_connect('http://example.org/ws') as ws:
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
if msg.data == 'close cmd':
await ws.close()
break
else:
await ws.send_str(msg.data + '/answer')
elif msg.type == aiohttp.WSMsgType.ERROR:
break

You must use the only websocket task for both reading (e.g. await ws.receive() or async for msg in ws:) and writing but may have multiple writer tasks which can only send data asynchronously (by await ws.send_str(‘data’) for example).

超时

超时设置存储在ClientTimeout对象中
默认情况下,这个超时的时间是5分钟
给seesion设置超时

1
2
3
timeout = aiohttp.ClientTimeout(total=60)
async with aiohttp.ClientSession(timeout=timeout) as session:
...

给这次get请求设置超时

1
2
async with session.get(url, timeout=timeout) as resp:
...

ClientTimeout接收多个参数作为超时标准

1
2
aiohttp.ClientTimeout(total=5*60, connect=None,
sock_connect=None, sock_read=None)
  • total
    总时长
  • connect
    建立新连接时间 或 在连接池中等待的时间
  • sock_connect
    A timeout for connecting to a peer for a new connection, not given from a pool
  • sock_read
    从对等方读取新数据部分之间的最大允许超时时间

代理

你可以在session.get()中设置你的proxy:

1
2
async with session.get(url, proxy=your_proxy_url) as response:
return BeautifulSoup(await response.content, 'html.parser')

如果你的IP需要认证,可以这样设置:

1
2
3
proxy = 'http://your_user:your_password@your_proxy_url:your_proxy_port'
async with session.get(url, proxy=proxy) as response:
return BeautifulSoup(await response.content, 'html.parser')

或者是这样设置:

1
2
3
4
proxy = 'http://your_proxy_url:your_proxy_port'
proxy_auth = aiohttp.BasicAuth('your_user', 'your_password')
async with session.get(url, proxy=proxy, proxy_auth=proxy_auth) as response:
return BeautifulSoup(await response.content, 'html.parser')

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!