S++

千线一眼

导航

python爬虫-异步协程

协程

1. 什么是协程

协程并不是计算机本身提供,而是由程序员人为创造。
协程也可以被称为微线程。是一种用户态上下文切换的技术。简而言之就是一个线程来回切换执行代码块。
协程实现的方法:现在主要推荐使用asyncawait关键字来实现(py3.6)

2. 协程的意义

在一个线程中,如果遇到IO等待时间,会自动利用空闲时间进行其他操作。

协程异步编程

1. 事件循环(event_loop)

理解为一个死循环,去检测并执行某些代码

解释
# 伪代码
tasks = [task1,task2,...,taskn]

while true:
  可执行任务列表, 已完成任务列表 = 检查tasks得到;
  for 就绪任务 in 可执行任务列表:
    执行就绪任务;
  for 完成任务 in 完成任务列表
    从tasks中移除完成任务;
  if tasks==[] 停止循环;
import asyncio

# 生成一个事件循环
loop = asyncio.get_event_loop()
# 将任务放到任务列表
loop.run_until_complete(tasks)

2. 协程对象(coroutine)

协程对象。我们可以将协程对象注册到事件循环中,它会被事件循环调用。我么可以使用 async 关键字来定义一个方法,这个方法在调用时不会立即被执行,而是返回一个协程对象。

3. 其它

task:任务。他是协程对象的进一步封装,包含了任务的各个状态。
future:表示将来执行或还未执行的任务,和 task没本质区别。
async:定义一个协程。
await:用来挂起阻塞方法的执行。

4. 样例

样例代码
import aiohttp
import asyncio


async def test_1():
    print('测试1.0')
    await asyncio.sleep(2)
    print('测试1.1')
    return 'test_1返回值'


async def test_2():
    print('测试2.0')
    await asyncio.sleep(2)
    print('测试2.1')
    return 'test_2返回值'


async def main():
    # 创建task,3.7用 asyncio.create_task()
    tasks = [
        asyncio.ensure_future(test_1()),
        asyncio.ensure_future(test_2())
    ]

    done, pending = await asyncio.wait(tasks, timeout=None)
    print(done, pending)


if __name__ == '__main__':
    # 生成一个事件循环
    loop = asyncio.get_event_loop()
    # 将任务放到任务列表
    loop.run_until_complete(main())
    # python3.7之后你还可以这样写
    # asyncio.run(main())
测试1.0
测试2.0
测试1.1
测试2.1
{<Task finished name='Task-2' coro=<test_1() done, defined at /Users/soutsukyou/PyCharm_Workspace/网络爬虫/study_async/study_async04.py:8> result='test_1返回值'>, <Task finished name='Task-3' coro=<test_2() done, defined at /Users/soutsukyou/PyCharm_Workspace/网络爬虫/study_async/study_async04.py:15> result='test_2返回值'>} set()

进程已结束,退出代码为 0

协程实践

1. 构建一个简单的Flask服务器

from flask import Flask
import time

app = Flask(__name__)


@app.route('/page1')
def index_page1():
    time.sleep(3)
    return 'Hello page1!'


@app.route('/page2')
def index_page2():
    time.sleep(3)
    return 'Hello page2!'


@app.route('/page3')
def index_page3():
    time.sleep(3)
    return 'Hello page3!'


if __name__ == '__main__':
    app.run(threaded=True)  # 这表明 Flask 启动了多线程模式,不然默认是只有一个线程的。

2. 模拟协程异步爬虫

"""
异步协程爬虫
注意版本:python 3.7 以上
"""
import time
import aiohttp
import asyncio


async def get_text(url):
    print('发起请求:', url)
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            text = await response.text()
            print('得到结果:', text)

if __name__ == '__main__':
    start_time = time.time()

    url_list = [
        'http://127.0.0.1:5000/page1',
        'http://127.0.0.1:5000/page2',
        'http://127.0.0.1:5000/page3'
    ]
    # 生成任务列表
    tasks = []
    for url in url_list:
        c = get_text(url)
        task = asyncio.ensure_future(c)
        tasks.append(task)
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))

    end_time = time.time()

    print('用时:', end_time - start_time)
发起请求: http://127.0.0.1:5000/page1
发起请求: http://127.0.0.1:5000/page2
发起请求: http://127.0.0.1:5000/page3
得到结果: Hello page1!
得到结果: Hello page2!
得到结果: Hello page3!
用时: 3.011800765991211

进程已结束,退出代码为 0

posted on 2022-03-17 19:23  S++  阅读(140)  评论(0)    收藏  举报