python并发编程笔记7
是根据蚂蚁学Python的视频做的笔记,方便自己后续回顾
视频链接:BV1bK411A7tV
老师的源码
这一份笔记对应的是视频的P11-P12
文章目录
P11-Python异步IO实现并发爬虫
1、协程的本质:在单线程内实现并发
线程是抢占式的,协程非抢占式,协程组织好的代码流程再运行,协程需要线程来承载运行。
多线程是假如你有一千个请求,线程启动时一瞬间全发出去,协程是依然是一个接着一个发,只不过在等待响应时切换到下一个请求任务。
线程调度是操作系统层面的,协程是应用层面做的。
2、Python 异步IO库介绍:asyncio
注意:
要用在异步IO编程中
依赖的库必须支持异步IO特性
爬虫引用中:
requests 不支持异步
需要用 aiohttp
import asyncio
获取事件循环
loop = asyncio.get_event_loop()
# 定义协程
async def myfunc(url):
await get_url(url)
# 创建task列表
tasks = [loop.create_task(myfunc(url)) for url in urls]
# 执行爬虫事件列表
loop.run_until_complete(asyncio.wait(tasks))
3、代码:
import asyncio
import aiohttp
import blog_spider
import time
# 定义协程
async def async_craw(url):
print("craw url:", url)
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
result = await resp.text()
print(f"craw url :{url},{len(result)}")
# 获取事件循环
loop = asyncio.get_event_loop()
# 创建task列表
tasks = [
loop.create_task(async_craw(url))
for url in blog_spider.urls]
start = time.time()
# 执行爬虫事件列表
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print("async_craw,cost", end - start, "秒")
对比总结:
通过运行01的代码运行时间的对比,多线程与协程对比的话,多线程花费的时间要比协程多,主要是因为切换线程的开销
P12-在异步IO中使用信号量控制爬虫并发度
使用信号量控制爬虫并发度的目的:
加入一定数量的并发度,防止我们的爬虫将目标网站爬坏,超出他的处理能力
信号量(英语:Semaphore)
信号量(英语:Semaphore)又称为信号量、旗语
是一个同步对象,用于保持在0至指定最大值之间的一个计数值。
- 当线程完成一次对该semaphore对象的等待(wait)时,该计数值减一;
- 当线程完成一次对semaphore对象的释放(release)时,计数值加一。
- 当计数值为0,则线程等待该semaphore对象不再能成功直至该semaphore对象变成signaled状态
- semaphore对象的计数值大于0,为signaled状态;计数值等于0,为nonsignaled状态.
使用方式1:
sem = asyncio.Semaphore(10)
# ... later
async with sem:
# work with shared resource
使用方式2:
sem = asyncio.Semaphore(10)
# ... later
await sem.acquire()
try:
# work with shared resource
finally:
sem.release()
代码:
import asyncio
import aiohttp
import blog_spider
import time
# 初始化一个信号量,10是并发度
semaphre = asyncio.Semaphore(10)
# 定义协程
async def async_craw(url):
# 使用信号量控制爬虫并发度:
async with semaphre:
print("craw url:", url)
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
result = await resp.text()
# 为了可视化运行情况,加个5秒
await asyncio.sleep(5)
print(f"craw url :{url},{len(result)}")
# 获取事件循环
loop = asyncio.get_event_loop()
# 创建task列表
tasks = [
loop.create_task(async_craw(url))
for url in blog_spider.urls]
start = time.time()
# 执行爬虫事件列表
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print("async_craw,cost", end - start, "秒")
r url in blog_spider.urls]
start = time.time()
# 执行爬虫事件列表
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print("async_craw,cost", end - start, "秒")