asxe
asxe

# 环境准备

python 版本 3.5 及以上,aiohttp 库

# 开始

# 基础了解

# 阻塞

阻塞是程序自身无法继续执行下一步的情况,即程序未得到所需计算资源而被挂起的状态
常见为网络 I/O 阻塞,磁盘 I/O 阻塞,以及使用者的输入阻塞等等

# 非阻塞

对比阻塞进行理解,即程序可以干别的事情,其是因阻塞的存在而存在的

# 同步

顾名思义,强制让不同的请求按顺序执行,即有序

# 异步

相对同步理解,即无序

# 多进程

利用 CPU 的多核,在同一时间内执行多个不同的任务

# 协程

协程本质上是单个进程,拥有自身的寄存器和栈,可以使用其实现异步操作

# 协程中的几个概念

event_loop:事件循环,可以将某些函数放到这上面,当运行条件满足时,就调用这个函数或者方法
coroutine:即协程,在 python 中,指代协程对象类型,可以将协程对象放到事件循环中。可以使用 async 关键字来定义一个方法,当调用这个方法时,并不会立即执行这个方法,而是返回一个协程对象
task:任务,是对协程对象的进一步封装,包含了协程对象的各个状态
future:即将执行或没有执行的任务结果,与 task 没有本质区别

# 举例

# 定义一个协程

定义协程
import asyncio
async def add(x):
    print(f'number:{x+1}')
coroutine=add(1)
print(f'1:{coroutine}')
print('now1')
event_loop=asyncio.new_event_loop()
event_loop.run_until_complete(coroutine)
print('now2')

注:以上代码第九行 event_loop=asyncio.new_event_loop () 也可以换成 event_loop=asyncio.get_event_loop (), 但是后者在新版 python 中会收到警告 DeprecationWarning: There is no current event loop event_loop=asyncio.get_event_loop ()
输出结果如下:

1:<coroutine object add at 0x0000025B7FECF840>
now1
number:2
now2

首先我们直接调用了 add 方法并打印其调用,但是我们得到的并不是答案,而是一个协程对象,接着我们使用 new_event_loop 方法创建了一个事件循环 event_loop,并调用 event_loop run_until_complete 方法,最终才看到 add 方法打印出的答案
此处也可以将 coroutine 封装成 task 对象(甚至可以不借助 event_loop 对象),此处不做解释,仅提供代码,请读者自行理解,如下

task
import asyncio
async def add(x):
    print(f'number:{x+1}')
coroutine=add(1)
loop=asyncio.new_event_loop()
task=loop.create_task(coroutine)
print(f'1:{task}')
print('now1')
loop.run_until_complete(task)
print(f'2:{task}')
print('now2')
"""
运行结果
1:<Task pending name='Task-1' coro=<add() running at F:\WorkSpace\py-case\main.py:3>>
now1
number:2
2:<Task finished name='Task-1' coro=<add() done, defined at F:\WorkSpace\py-case\main.py:3> result=None>
now2
"""
不借助loop对象的task
import asyncio
async def add(x):
    print(f'number:{x+1}')
coroutine=add(1)
task=asyncio.ensure_future(coroutine)
print(f'1:{task}')
print('now1')
loop=asyncio.get_event_loop()
loop.run_until_complete(task)
print(f'2:{task}')
print('now2')
"""
运行结果
1:<Task pending name='Task-2' coro=<add() running at F:\WorkSpace\py-case\main.py:18>>
now1
number:2
2:<Task finished name='Task-2' coro=<add() done, defined at F:\WorkSpace\py-case\main.py:18> result=None>
now2
"""

# 多任务协程

多任务协程
import asyncio
import requests
async def get_response():
    url='https://www.baidu.com'
    res=requests.get(url)
    return res
tasks=list(asyncio.ensure_future(get_response()) for _ in range(3))
print(f'task:{tasks}')
loop=asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
for task in tasks:
    print(f'result:{task.result()}')

此处我们创建 3 个请求并组成列表,然后通过 wait 方法将其放到事件循环中,即可发起 3 次请求

但是此时仍然是没有异步处理的,因为 requests 库并不支持异步,需要换成 aiohttp,况且上文还说了,实现异步就得有挂起操作,实现如下:

异步
import aiohttp
import asyncio
import time
start_time=time.time()
async def get_url(url):
    session=aiohttp.ClientSession()
    res=await session.get(url)
    await res.text()
    await session.close()
    return res
async def req():
    url='https://www.httpbin.org/delay/5'
    print('waiting',url)
    res=await get_url(url)
    print(f'{res} - {url}')
tasks=list(asyncio.ensure_future(req()) for _ in range(6))
event_loop=asyncio.get_event_loop()
event_loop.run_until_complete(asyncio.wait(tasks))
end_time=time.time()
time_consuming=end_time-start_time
print(time_consuming)

此代码即可实现异步操作,其中 await 关键字的作用就是将协程挂起,可看到,原本需要至少 30 秒的请求时间只用了 10 秒,大大提升了爬取速度,(耗时与网络状况有关)
1.jpg

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

ASXE 微信支付

微信支付

ASXE 支付宝

支付宝

ASXE 贝宝

贝宝