概念
- 协程能够更加高效的利用CPU,比多线程更加效率
- 多线程在执行IO操作的时候只能等待,然后CPU在IO操作结束继续后序操作的时候要在多个线程之前来回调度,会消耗部分资源
- 协程是用一个线程,当一个操作进入IO操作等待的时候,就讲该操作放到一边,执行另一个操作,这样就减少了线程切换的消耗
使用
-
引入 import asyncio
-
协程对象想要执行,必须借助event_loop: event_loop = asyncio.get_event_loop()
import asyncio # 该函数执行的时候返回的是一个协程对象 async def foo(): print("函数执行") if __name__ == '__main__': # 协程对象要想执行,必须借助于event_loop f = foo() # 拿到事件循环 event_loop = asyncio.get_event_loop() # event_loop执行协程对象,直到该对象内的内容执行完毕为止 event_loop.run_until_complete(f) # 也可以执行调用run, 但是其在内容调用了close方法,可能在windows系统上报错 asyncio.run(f) -
实际用途
import asyncio async def func1(): print('func1开始') await asyncio.sleep(1) print('func1结束') async def func2(): print('fun2开始') await asyncio.sleep(2) print('func2结束') async def func3(): print('func3开始') await asyncio.sleep(3) print('func3结束') if __name__ == '__main__': f1 = func1() f2 = func2() f3 = func3() tasks = [f1,f2,f3] asyncio.run(asyncio.wait(tasks)) -
返回值
- done,pending = await asyncio.wait(tasks) : 返回值没有顺序
- result = await asyncio.gather(*tasks,return_exceptions=True) : 返回值有顺序(按照tasks中添加任务的顺序)
- return_exceptions:
- Ture:如果有错误信息,则打印错误信息,后序任务继续
- False: 如果有错误信息,则报错,所有任务停止
异步爬虫
需要用到2个库
- aiohttp:异步网络请求的
- aiofiles:异步操作IO文件的
async def download(url):
print('开始下载',url)
file_name = url.split("/")[-1]
# 相当于requests
async with aiohttp.ClientSession() as session:
#发送网络请求
async with session.get(url) as resp:
# await resp.text() #=> resp.text
content = await resp.content.read() # => resp.content
# 写入文件
async with aiofiles.open(file_name,mode="wb") as f:
await f.write(content)
print("下载完成",url)
async def main():
urls = [
'http://wwwxxx.jpg',
'http://wwwxxx.jpg',
'http://wwwxxx.jpg',
'http://www.xxx.jpg'
]
tasks = []
for item in urls:
t = asyncio.create_task(download(item))
tasks.append(t)
await asyncio.wait(tasks)
if __name__ == '__main__':
asyncio.run(main())
2023年08月16日测试发现async with aiohttp.ClientSession() as session:在获取文本的时候报错,无法链接到https://xxxcom:443相关问题 解决办法:
- 引入
from aiohttp import TCPConnector - 替换成
async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(limit=64, verify_ssl=False)) as session: - 然后解析返回值的时候加上errors='ignore'
html = await resp.text(errors='ignore') - 然后就可以继续愉快的爬虫了