Python进阶:聊协程
从一个爬虫说起
import time def crawl_page(url):
print('crawling {}'.format(url))
sleep_time = int(url.split('_')[-1])
time.sleep(sleep_time)
print('OK {}'.format(url)) def main(urls):
for url in urls:
crawl_page(url) %time main(['url_1', 'url_2', 'url_3', 'url_4']) ######### 输出 ########## crawling url_1
OK url_1
crawling url_2
OK url_2
crawling url_3
OK url_3
crawling url_4
OK url_4
Wall time: 10 s
import asyncio
import nest_asyncio
nest_asyncio.apply() async def crawl_page(url):
print('crawling {}'.format(url))
sleep_time = int(url.split('_')[-1])
await asyncio.sleep(sleep_time)
print('OK {}'.format(url)) async def main(urls):
for url in urls:
await crawl_page(url) %time asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4'])) ########## 输出 ########## crawling url_1
OK url_1
crawling url_2
OK url_2
crawling url_3
OK url_3
crawling url_4
OK url_4
Wall time: 10 s
首先来看 import asyncio,这个库包含了大部分我们实现协程所需的魔法工具。async 修饰词声明异步函数,于是,这里的 crawl_page 和 main 都变成了异步函数。可以通过 await 来调用异步函数。await 执行的效果和 Python 正常执行是一样的,也就是说程序会阻塞在这里,进入被调用的协程函数,执行完毕返回后再继续,而这也是 await 的字面意思。代码中 await asyncio.sleep(sleep_time) 会在这里休息若干秒,await crawl_page(url) 则会执行 crawl_page() 函数。
上面的代码仍然是同步执行的,所以同样是用了10秒。
如果要异步执行,可以用Task任务的概念。
import asyncio
import nest_asyncio
nest_asyncio.apply() async def crawl_page(url):
print('crawling {}'.format(url))
sleep_time = int(url.split('_')[-1])
await asyncio.sleep(sleep_time)
print('OK {}'.format(url)) async def main(urls):
tasks = [asyncio.create_task(crawl_page(url)) for url in urls]
for task in tasks:
await task %time asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4'])) ######### 输出 ########## crawling url_1
crawling url_2
crawling url_3
crawling url_4
OK url_1
OK url_2
OK url_3
OK url_4
Wall time: 4.01 s
在create_task创建任务后,任务就会马上由事件循环调度执行。如果不调用await task,代码就不会阻塞。我们想要等所有任务结束再往下走,因此用for task in tasks: await task。现在时间就4秒左右,可以用await asyncio.gather(*tasks)来代替for task in tasks: await task。
import asyncio
import nest_asyncio
nest_asyncio.apply()
import time async def crawl_page(url):
print('crawling {}'.format(url))
sleep_time = int(url.split('_')[-1])
#await asyncio.sleep(sleep_time)
await asyncio.sleep(sleep_time)
print('OK {}'.format(url)) async def main(urls):
tasks = [asyncio.create_task(crawl_page(url)) for url in urls ]
await asyncio.gather(*tasks) %time asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))
协程运行时出现错误,要怎么处理?
看代码:
import asyncio
import nest_asyncio
nest_asyncio.apply() async def worker_1():
await asyncio.sleep(1)
return 1 async def worker_2():
await asyncio.sleep(2)
return 2 / 0 async def worker_3():
await asyncio.sleep(3)
print('over worker_3')
return 3 async def main():
task_1 = asyncio.create_task(worker_1())
task_2 = asyncio.create_task(worker_2())
task_3 = asyncio.create_task(worker_3()) await asyncio.sleep(2)
task_3.cancel() res = await asyncio.gather(task_1, task_2, task_3, return_exceptions=True)
print(res) %time asyncio.run(main()) ########## 输出 ########## # [1, ZeroDivisionError('division by zero'), CancelledError()]
# Wall time: 2 s
注意return_exceptions参数,上面代码在执worker_2会抛出除0的异常,而worker_2中没有做try..catch捕捉错误,本应该会程序会停止,由于设置了return_exceptions=True,所以没有影响到其他任务的执行。而 CancelledError()表示task_3被cancel()取消掉.
协程实现消费者生产者模型
show the code:
import asyncio
import random async def consumer(queue, id):
while True:
val = await queue.get()
print('{} get a val: {}'.format(id, val))
await asyncio.sleep(1) async def producer(queue, id):
for i in range(5):
val = random.randint(1, 10)
await queue.put(val)
print('{} put a val: {}'.format(id, val))
await asyncio.sleep(2) async def main():
queue = asyncio.Queue() consumer_1 = asyncio.create_task(consumer(queue, 'consumer_1'))
consumer_2 = asyncio.create_task(consumer(queue, 'consumer_2')) producer_1 = asyncio.create_task(producer(queue, 'producer_1'))
producer_2 = asyncio.create_task(producer(queue, 'producer_2')) await asyncio.sleep(10)
consumer_1.cancel()
consumer_2.cancel() await asyncio.gather(consumer_1, consumer_2, producer_1, producer_2, return_exceptions=True) %time asyncio.run(main()) ########## 输出 ########## # producer_1 put a val: 5
# producer_2 put a val: 3
# consumer_1 get a val: 5
# consumer_2 get a val: 3
# producer_1 put a val: 1
# producer_2 put a val: 3
# consumer_2 get a val: 1
# consumer_1 get a val: 3
# producer_1 put a val: 6
# producer_2 put a val: 10
# consumer_1 get a val: 6
# consumer_2 get a val: 10
# producer_1 put a val: 4
# producer_2 put a val: 5
# consumer_2 get a val: 4
# consumer_1 get a val: 5
# producer_1 put a val: 2
# producer_2 put a val: 8
# consumer_1 get a val: 2
# consumer_2 get a val: 8
# Wall time: 10 s
实战:豆瓣近日推荐电影爬虫
import requests
from bs4 import BeautifulSoup def main():
url = "https://movie.douban.com/cinema/later/beijing/"
init_page = requests.get(url).content
init_soup = BeautifulSoup(init_page, 'lxml') all_movies = init_soup.find('div', id="showing-soon")
for each_movie in all_movies.find_all('div', class_="item"):
all_a_tag = each_movie.find_all('a')
all_li_tag = each_movie.find_all('li') movie_name = all_a_tag[1].text
url_to_fetch = all_a_tag[1]['href']
movie_date = all_li_tag[0].text response_item = requests.get(url_to_fetch).content
soup_item = BeautifulSoup(response_item, 'lxml')
img_tag = soup_item.find('img') print('{} {} {}'.format(movie_name, movie_date, img_tag['src'])) %time main() ########## 输出 ##########
九龙不败 07月02日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2560169035.jpg
善良的天使 07月02日 https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2558266159.jpg
别岁 07月02日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2558138041.jpg
上海的女儿 07月02日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2555602094.jpg
爱宠大机密2 07月05日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2555923582.jpg
扫毒2天地对决 07月05日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2560684734.jpg
猪猪侠·不可思议的世界 07月05日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2560664101.jpg
他她他她 07月05日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2559292102.jpg
狮子王 07月12日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2559658750.jpg
命运之夜——天之杯II :迷失之蝶 07月12日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2560749451.jpg
宝莱坞机器人2.0:重生归来 07月12日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2558657891.jpg
素人特工 07月12日 https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2560447448.jpg
机动战士高达NT 07月12日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2558661806.jpg
舞动吧!少年 07月12日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2555119986.jpg
嘿,蠢贼 07月16日 https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2560832388.jpg
银河补习班 07月18日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2560954373.jpg
小小的愿望 07月18日 https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2560659129.jpg
匠心 07月18日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2553935771.jpg
猪八戒·传说 07月19日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2559590242.jpg
刀背藏身 07月19日 https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2557644589.jpg
为家而战 07月19日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2559337905.jpg
Wall time: 22.1 s
import asyncio
import aiohttp from bs4 import BeautifulSoup async def fetch_content(url):
async with aiohttp.ClientSession(
headers=header, connector=aiohttp.TCPConnector(ssl=False)
) as session:
async with session.get(url) as response:
return await response.text() async def main():
url = "https://movie.douban.com/cinema/later/beijing/"
init_page = await fetch_content(url)
init_soup = BeautifulSoup(init_page, 'lxml') movie_names, urls_to_fetch, movie_dates = [], [], [] all_movies = init_soup.find('div', id="showing-soon")
for each_movie in all_movies.find_all('div', class_="item"):
all_a_tag = each_movie.find_all('a')
all_li_tag = each_movie.find_all('li') movie_names.append(all_a_tag[1].text)
urls_to_fetch.append(all_a_tag[1]['href'])
movie_dates.append(all_li_tag[0].text) tasks = [fetch_content(url) for url in urls_to_fetch]
pages = await asyncio.gather(*tasks) for movie_name, movie_date, page in zip(movie_names, movie_dates, pages):
soup_item = BeautifulSoup(page, 'lxml')
img_tag = soup_item.find('img') print('{} {} {}'.format(movie_name, movie_date, img_tag['src'])) %time asyncio.run(main()) ########## 输出 ########## 九龙不败 07月02日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2560169035.jpg
善良的天使 07月02日 https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2558266159.jpg
别岁 07月02日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2558138041.jpg
上海的女儿 07月02日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2555602094.jpg
爱宠大机密2 07月05日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2555923582.jpg
扫毒2天地对决 07月05日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2560684734.jpg
猪猪侠·不可思议的世界 07月05日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2560664101.jpg
他她他她 07月05日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2559292102.jpg
狮子王 07月12日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2559658750.jpg
命运之夜——天之杯II :迷失之蝶 07月12日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2560749451.jpg
宝莱坞机器人2.0:重生归来 07月12日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2558657891.jpg
素人特工 07月12日 https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2560447448.jpg
机动战士高达NT 07月12日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2558661806.jpg
舞动吧!少年 07月12日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2555119986.jpg
嘿,蠢贼 07月16日 https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2560832388.jpg
银河补习班 07月18日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2560954373.jpg
小小的愿望 07月18日 https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2560659129.jpg
匠心 07月18日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2553935771.jpg
猪八戒·传说 07月19日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2559590242.jpg
刀背藏身 07月19日 https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2557644589.jpg
为家而战 07月19日 https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2559337905.jpg
Wall time: 5.82 s
参考
极客时间《Python核心技术与实战》专栏
Python进阶:聊协程的更多相关文章
- Python 进阶 之 协程
协程的概念级描述(与线程对比):转自知乎 链接 线程有两个必须要处理的问题:一是碰着阻塞式I\O会导致整个进程被挂起: 二是由于缺乏时钟阻塞,进程需要自己拥有调度线程的能力. 如果一种实现使得每个线程 ...
- Python自动化 【第十篇】:Python进阶-多进程/协程/事件驱动与Select\Poll\Epoll异步IO
本节内容: 多进程 协程 事件驱动与Select\Poll\Epoll异步IO 1. 多进程 启动多个进程 进程中启进程 父进程与子进程 进程间通信 不同进程间内存是不共享的,要想实现两个进程间 ...
- Python 进程线程协程 GIL 闭包 与高阶函数(五)
Python 进程线程协程 GIL 闭包 与高阶函数(五) 1 GIL线程全局锁 线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的 ...
- 第十一章:Python高级编程-协程和异步IO
第十一章:Python高级编程-协程和异步IO Python3高级核心技术97讲 笔记 目录 第十一章:Python高级编程-协程和异步IO 11.1 并发.并行.同步.异步.阻塞.非阻塞 11.2 ...
- Python中Paramiko协程方式详解
什么是协程 协程我们可以看做是一种用户空间的线程. 操作系统对齐存在一无所知,需要用户自己去调度. 比如说进程,线程操作系统都是知道它们存在的.协程的话是用户空间的线程,操作系统是不知道的. 为什么要 ...
- [转载]Python 3.5 协程究竟是个啥
http://blog.rainy.im/2016/03/10/how-the-heck-does-async-await-work-in-python-3-5/ [译] Python 3.5 协程究 ...
- [译] Python 3.5 协程究竟是个啥
转自:http://blog.rainy.im/2016/03/10/how-the-heck-does-async-await-work-in-python-3-5/ [译] Python 3.5 ...
- python中的协程及实现
1.协程的概念: 协程是一种用户态的轻量级线程.协程拥有自己的寄存器上下文和栈. 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切换回来的时候,恢复先前保存的寄存器上下文和栈. 因此,协程能保留 ...
- Python基础之协程
阅读目录 一 引子 二 协程介绍 三 Greenlet模块 四 Gevent模块 引子 之前我们学习了线程.进程的概念,了解了在操作系统中 进程是资源分配的最小单位,线程是CPU调度的最小单位. 按道 ...
随机推荐
- Ubuntu16.04启动tomcat缓慢问题之解决方案
问题信息: -May- ::] org.apache.catalina.util.SessionIdGeneratorBase.createSecureRandom Creation of Secur ...
- apache不能解析php之解决办法
记录一下Ubuntu16.04下的apache服务器不能解析php的问题,如图所示: 对于这个Ubuntu16.04最直接最快速的解决办法就是执行如下命令: sudo apt-get install ...
- Unexpected token o in JSON at position 1 报错原因
写在前面的话这个问题在之前做项目时碰到过一次,当时按照网上的做法,去掉JSON.parse()这一层转换后就没有这个报错了,数据也能正常使用,就没多想,也没深究是什么原因. 可是这次又碰到了,所以这次 ...
- No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK? idea maven 打包报错问题解决
mvn clean install -X -Dmaven.test.skip=true -P dev 打包报错:No compiler is provided in this environment. ...
- leetcode 576. Out of Boundary Paths 、688. Knight Probability in Chessboard
576. Out of Boundary Paths 给你一个棋盘,并放一个东西在一个起始位置,上.下.左.右移动,移动n次,一共有多少种可能移出这个棋盘 https://www.cnblogs.co ...
- typescript属性类型接口
/* typeScript中的接口 - 1.属性类接口 */ /* 接口的作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用.接口定 ...
- linux硬盘分区与格式化
linux硬盘分区与格式化: 1. 设备管理 在 Linux 中,每一个硬件设备都映射到一个系统的文件,对于硬盘.光驱等 IDE 或 SCSI 设备也不例外. Linux 把各种 IDE 设备分配了一 ...
- Java8的时间日期API
原先的时间 api 大部分已经过时了 Date构造器 需要传入年月日 但是对时间的加减操作比较麻烦 Calenda 加减比较方便 使用 LocalDate. LocalTime. LocalDa ...
- 泡泡一分钟:Project AutoVision - Localization and 3D Scene Perception for an Autonomous Vehicle with a Multi-Camera System
Project AutoVision - Localization and 3D Scene Perception for an Autonomous Vehicle with a Multi-Cam ...
- 全面系统Python3入门+进阶_汇总
https://coding.imooc.com/class/136.html#Anchor 全面系统Python3入门+进阶-1-1 导学 全面系统Python3入门+进阶-1-2 Python的特 ...