1:GIL 是什么?
全局解释器锁 (英语: Global Interpreter Lock,缩写GIL)是计算机程序设计语言解释器用于同步线程的一种机制,它使得任何时刻仅有一个线程在执行即便在多核心处理器上,使用 GIL 的解释器也只允许同一时间执行一个线程。
2:为什么有GIL这个东西?
原因
简而言之: Python设计初期,为了规避并发问题引入了GIL,现在想去除却去不掉了!
为了解决多线程之间数据完整性和状态同步问题Python中对象的管理
3存在问题
由于GIL的存在
即使电脑有多核CPU
单个时刻也只能使用1个
相比并发加速的C++/JAVA所以慢
4:怎样规避GIL带来的限制?
1、多线程 threading 机制依然是有用的,用于IO密集型计算
因为在 I/0 (read,write,send,recv,etc.)期间,线程会释放GIL,实现CPU和IO的并行因此多线程用于IO密集型计算依然可以大幅提升速度
但是多线程用于CPU密集型计算时,只会更加拖慢速度
2:来到了这里我们开始说异步协程
5:协程
基本步骤
#1:导入包
import asyncio
#2定义协程函数
async def demo(data):
print(data)
get=demo(1)#协程对象
#3:获取事件循环
loop=asyncio.get_event_loop()
loop.run_until_complete(get)#可以放进去协程对象
"""在python3.7以上提供了更简便的方法"""
#3:获取事件循环
asyncio.run(get)#这一行代码可以代替9,10二行的代码
"""
注意:
执行协程函数创建协程对象,
函数内部代码不会执行,如果想要运行协程函数内部代码
必须要讲协程对象交给事件循环来处理
"""
await的简单介绍:
await后面可以+可以等待的对象(协程对象,Future,Task对象>>>io等待);
等待这个io,当这个io操作完成后代码继续往下走,(没有完成不走下面的代码)
import asyncio
async def others() :
print("start")
await asyncio.sleep(2)
print('end')
return"返回值"
async def func():
print("执行协程函数内部代码")
"""
遇到IO操作挂起当前协程(任务),
等IO操作完成之后再继续往下执行。
当前协程挂起时,事件循环可以去执行其他协程 (任务)。
"""
response1 = await others()
print("IO请求结束,结果为:",response1)
response2 = await others()
print("IO请求结束,结果为:", response2)
asyncio.run(func())
#结果为:
执行协程函数内部代码
start
end
IO请求结束,结果为: 返回值
start
end
IO请求结束,结果为: 返回值
await就是等待对象的值得到结果之后再继续向下走。
task对象的简单介绍:
在事件循环中添加多个任务的.
Tasks用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环中等待被调度执行。除了使用asyncio.create_task(函数以外,还可以用低层级的loop.create_task0或ensure_future(函数。不建议手动实例化 Task 对象。
注意:asyncio.create_task0 函数在 Python 3.7 中被加入。在 Python 3.7之前,可以改用低层级的asyncio.ensure_future()函数
import asyncio
async def func():
print(1)
await asyncio.sleep(2)
print(2)
return"返回值"
async def main() :
print("main开始")
task_Tist =[
asyncio.create_task(func(),name='n1'),
asyncio.create_task(func(),name='n2')
]
print("main结束")
done,pending = await asyncio.wait(task_Tist)
print(done)
asyncio.run(main())
#结果为:
main开始
main结束
1
1
2
2
{<Task finished name='n1' coro=<func() done, defined at D:/py-work/dasdas.py:41> result='返回值'>, <Task finished name='n2' coro=<func() done, defined at D:/py-work/dasdas.py:41> result='返回值'>}
await里面不能放列表,所以把他放asyncin.wait()里面
import asyncio
async def func():
print(1)
await asyncio.sleep(2)
print(2)
return "返回值"
task_list =[
func() ,
func()
]
done,pending= asyncio.run( asyncio.wait(task_list) )
print(done)
#结果为:
1
1
2
2
({<Task finished name='Task-2' coro=<func() done, defined at D:/py-work/dasdas.py:41> result='返回值'>, <Task finished name='Task-3' coro=<func() done, defined at D:/py-work/dasdas.py:41> result='返回值'>}, set())
案例
import asyncio
import aiohttp #发异步请求的把包
urls = [
"url1",
"url2",
url3,
]
async def aiodownload(url):
name = url.rsplit('/', 1)[1]#从右往左用“/”切,切1刀,成2份,然后取第2个位置的元素:ptgvyyrnvgn.jpg;使用"/",/做分隔符
# 因为异步协程,需要用async,又因为request不用close可以用上下文管理器with,所以这么写
async with aiohttp.ClientSession() as session:#aiohttp.ClientSession()=requests()
async with session.get(url) as resp: # 相当于:resp.get()
# 这里请求成功,开始写入文件
# resp.content.read() # 相当于 resp.content
# resp.text() # 相当于resp.text
# resp.json() # 相当于resp.json()
with open(name, mode='wb') as f:
f.write(await resp.content.read()) # 保存文件,并用await挂起
print('结束')
# s = aiohttp.ClientSession <==> requests 异步协程请求的这相当于requests
# 发送请求
# 得到图片
# 保存内容
#rsplit将字符串拆分为列表
async def main():
#tasks = []
#for url in urls:
#tasks.append(asyncio.create_task(aiodownload(url)))
#await asyncio.wait(tasks)
#或者为下面这个格式也行
tasks=[asyncio.create_task(aiodownload(url))for url in urls]
await asyncio.wait(tasks)
if __name__ == '__main__':
asyncio.run(main())
#下面这个写法在python3.10中不使用了
#loop = asyncio.get_event_loop() # asyncio.get_event_loop():获取一个事件循环
#loop.run_until_complete(main()) # run_until_complete():将任务放到任务列表
import requests
import asyncio
import aiohttp
import aiofiles #可以使用中创建异步的包
def get_3m3u8(url):
headers = {
"user-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.52",
'referer':"https://vip.dysmz.top/"
}
resp=requests.get(url=url,headers=headers).text
with open ('get_sencod_m3u8',mode='w',encoding='utf-8')as fp:
fp.write(resp)
asyncio.run(aiodowblong(resp))
def get_sencod_m3u8(url):
headers = {
"user-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.52"
}
with open('get_first_m3u8',mode='r',encoding='utf-8')as fp:
for m3u81 in fp:
line=m3u81.strip()#移除字符串头尾指定的字符;该方法只能删除开头或是结尾的字符
if line.startswith("#"):#如果以#开头的话
continue
aiodownlong_sencod_M3U8=url+line
get_3m3u8(aiodownlong_sencod_M3U8)
async def aiodown0(url,name):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
async with aiofiles.open(f"./video/{name}", mode="wb") as fp:
await fp.write(await resp.content.read())
print(f"{name}下载完成")
async def aiodowblong(url):#拿所以的下载网站
tasks=[]
async with aiofiles.open("get_sencod_m3u8",mode="r",encoding="utf-8")as fp:
async for line in fp:
if line.startswith('#'):
continue
line=line.strip()
name=line.split("hls/")[1]
tasks.append(asyncio.create_task(aiodown0(line,name)))
await asyncio.wait(tasks)
def get_first_m3u8(url):
headers = {
"user-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.52"
}
resp=requests.get(url=url,headers=headers).text
with open("get_first_m3u8",mode="w",encoding="utf-8")as fp:
fp.write(resp)
if __name__=="__main__":
url='https://new.qqaku.com/20220819/tHStWHoI/index.m3u8'
first_m3u8_url=url.split("/20220819/")[0]
get_first_m3u8(url)
get_sencod_m3u8(first_m3u8_url)
import requests
import aiohttp
import os
import asyncio
import time
if not os.path.exists('./西游记'):
os.mkdir('./西游记')
async def aiodownolong(cid,title):
url="........."
async with aiohttp.ClientSession()as session:
async with session.get(url)as resp:
d=await resp.json()
with open(f'./西游记/{title}',mode="w",encoding="utf-8")as fp:
fp.write(d['data']['novel']['content'])
print(f'{title}爬取成功!!')
async def get_cat(url):
heard = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.26"
}
resp=requests.get(url=url,headers=heard)
dic=resp.json()
task=[]
for item in dic['data']['novel']['items']:
title=item['title']
cid=item['cid']
task.append(asyncio.create_task(aiodownolong(cid,title)))
await asyncio.wait(task)
if __name__ == '__main__':
a=time.time()
url='.......'
b=time.time()
asyncio.run(get_cat(url))
print(b-a)
import requests
import os
import asyncio
import aiofiles #可以创建一个异步的包
import aiohttp
async def tupian(page):
data = {
'per_page': '12',
'page': page,
'seo_tags': 'true'
}
heard={
"secret-key": 'H2jk9uKnhRmL6WPwh89zBezWvr',
"referer": "https://www.pexels.com/zh-cn/@tkirkgoz/",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"
}
url="..........."
request=requests.get(url=url,headers=heard,params=data) #params参数
request.encoding="utf-8"
request=request.json()
data_data=request["data"]
task=[]
for id in data_data:
id=id["id"]
task.append(asyncio.create_task(downlong(id)))
await asyncio.wait(task)
async def downlong(id):
# 创建一个文件夹(包)
if not os.path.exists('./picture'):
os.mkdir('./picture')
url=f"https://images.pexels.com/photos/{id}/pexels-photo-{id}.jpeg?auto=compress&cs=tinysrgb&w=150&lazy=load"
async with aiohttp.ClientSession() as session:
async with session.get(url=url)as resp:
async with aiofiles.open(f"./picture/+{id}.jpg",mode="wb")as fp:
# with open(f"./picture/+{id}.jpg",mode="wb")as fp:
fp.write(await resp.content.read())
print(f"{id}下载成功")
def run():
a=int(input("请输入你要爬取的页数:"))
for page in range(3,a+1):
#asyncio.run(tupian(page)) #报错Event loop is closed
loop = asyncio.get_event_loop()
loop.run_until_complete(tupian(page))
"""
总而言之是asyncio.run()会自动关闭循环,
并且调用_ProactorBasePipeTransport.__del__报错,
而asyncio.run_until_complete()不会.
"""
if __name__ =="__main__":
run()