python爬虫-异步协程
协程
1. 什么是协程
协程并不是计算机本身提供,而是由程序员人为创造。
协程也可以被称为微线程。是一种用户态上下文切换的技术。简而言之就是一个线程来回切换执行代码块。
协程实现的方法:现在主要推荐使用async和await关键字来实现(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
浙公网安备 33010602011771号