单线程+多任务异步协程:asyncio 3.6
- 事件循环
- 无限循环的对象.事件循环中最终需要将一些 特殊的函数(被async关键字修饰的函数) 注册在该对象中.
- 协程
- 本质上是一个对象.可以把协程对象(特殊的函数)注册到事件循环中
- 任务对象
- 就是对协程对象进一步的封装.
- 绑定回调: task.add_done_callback(func)
- func(task):task参数表示的就是绑定的任务对象
- task.result():返回就是任务对象对应的特殊函数内部的返回值
- 回调多被用作于爬虫中的解析方法
- await
- 在任务对象对应的特殊函数内部的实现语句中,如果出现了阻塞的操作,则必须使用await进行修饰
- 异步操作的体现
- 当将多个协程对象(特殊的函数)注册到事件循环中后,事件循环开启后,则会循环执行其内部的协程对象们
- 假如事件循环对象在执行某一个协程对象时,发生了阻塞,则事件循环对象不会等待阻塞结束,反而会执行下一个协程对象
- aiohttp:支持异步的网络请求模块
- 中文文档
- https://www.cntofu.com/book/127/aiohttp%E6%96%87%E6%A1%A3/ClientUsage.md
- 环境安装 pip或者直接pycharm安装都可以
- 如何进行UA伪装:
- session.get(url,headers)
- 参数的封装
- session.get(url,headers,data/params)
- 代理IP方式:
- session.get(url,proxy="http://ip:port")
- 中文文档
简单示例
import asyncio #特殊的函數:该函数调用后,函数内部的程序语句不会被执行,但是该函数调用会返回一个协程对象 async def test():print('i am test()')print('i am test()')print('i am test()')#调用该特殊函数,让其返回一个协程对象 c = test()#创建一个事件循环对象 loop = asyncio.get_event_loop()#将协程对象注册到事件循环对象中,并且开启事件循环 loop.run_until_complete(c)print(c)
任务对象的使用
import asyncio #特殊的函數:该函数调用后,函数内部的程序语句不会被执行,但是该函数调用会返回一个协程对象 async def test():print('i am test()')#调用该特殊函数,让其返回一个协程对象 c = test()#将协程对象封装到任务对象中 task = asyncio.ensure_future(c)#创建一个事件循环对象 loop = asyncio.get_event_loop()#将任务对象注册到事件循环对象中,并且开启事件循环 loop.run_until_complete(task)
任务对象绑定回调函数
import asyncio #特殊的函數:该函数调用后,函数内部的程序语句不会被执行,但是该函数调用会返回一个协程对象 async def test():print('i am test()')return 'hello bobo'#任务对象的回调函数,参数task表示的就是任务对象 def func(task):# print('i am task callback!')print(task.result()) #返回的是任务对象对应的特殊函数的返回值#调用该特殊函数,让其返回一个协程对象 c = test()#将协程对象封装到任务对象中 task = asyncio.ensure_future(c)#给任务对象绑定一个回调函数 task.add_done_callback(func)#创建一个事件循环对象 loop = asyncio.get_event_loop()#将任务对象注册到事件循环对象中,并且开启事件循环 loop.run_until_complete(task)
多任务异步协程
import asyncio import time #函数内部不可以出现不支持异步模块的代码 #该函数内部的异步操作必须使用await进行修饰 async def request(url):print('正在下载:',url)# time.sleep(2) #time模块是一个不支持异步的模块await asyncio.sleep(2) #asyncio模块中提供的一个支持异步的阻塞方法print(url,'下载完毕!')return url#创建一个回调函数 def callback(task):#返回的是任务对象对应的特殊函数的返回值print(task.result())urls = ['www.1.com','www.2.com','www.3.com','www.4.com', ] #记录开始时间 start = time.time() #任务列表 tasks = [] for url in urls:#调用该特殊函数,让其返回一个协程对象c = request(url)#将协程对象封装到任务对象中task = asyncio.ensure_future(c)# 给任务对象绑定回调task.add_done_callback(callback)#将任务对象添加到列表中tasks.append(task)#创建一个事件循环对象 loop = asyncio.get_event_loop() #将任务对象列表注册到事件循环对象中,并且开启事件循环 loop.run_until_complete(asyncio.wait(tasks)) ##记录结束时间 print(time.time()-start)
单线程+多任务异步协程的爬虫
import asyncio import requests import time import aiohttp from lxml import etree urls = ['http://localhost:5000/bobo','http://localhost:5000/jay','http://localhost:5000/tom','http://localhost:5000/bobo','http://localhost:5000/jay','http://localhost:5000/tom' ]# async def get_page(url): # #requests模块是一个不支持异步的模块,解决方法就是使用一个支持异步的模块进行请求发送 # page_text = requests.get(url=url).text # return page_textasync def get_page(url):#使用aiohttp进行请求发送#实例化了一个发送网络请求的对象async with aiohttp.ClientSession() as session:#该函数内部的异步操作必须使用await进行修饰async with await session.get(url) as response:#获取响应数据(页面源码数据)page_text = await response.text()# print(page_text)return page_text #数据解析的操作需要在回调函数中实现 def parse(task):page_text = task.result()tree = etree.HTML(page_text)parse_data = tree.xpath('//body/text()')[0]print(parse_data)start = time.time() tasks = [] for url in urls:#调用该特殊函数,让其返回一个协程对象c = get_page(url)#将协程对象封装到任务对象中task = asyncio.ensure_future(c)# 给任务对象绑定回调task.add_done_callback(parse)#将任务对象添加到列表中tasks.append(task) #创建一个事件循环对象 loop = asyncio.get_event_loop() #将任务对象列表注册到事件循环对象中,并且开启事件循环 loop.run_until_complete(asyncio.wait(tasks))print(time.time()-start)
单线程+多任务异步协程的应用
#爬取喜马拉雅中的相声音频 import requests import aiohttp import asyncio #通用的url模板 url = 'https://www.ximalaya.com/revision/play/album?albumId=19366477&pageNum=%d&sort=1&pageSize=2' headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36' } #获取了所有即将被下载的音频连接 urls = [] for page in range(1,3):new_url = format(url%page)dic_obj = requests.get(url=new_url,headers=headers).json()for dic in dic_obj['data']['tracksAudioPlay']:audio_url = dic['src']urls.append(audio_url)#特殊的函數:该函数调用后,函数内部的程序语句不会被执行,但是该函数调用会返回一个协程对象 async def get_audio_data(url):#使用aiohttp进行请求发送#实例化了一个发送网络请求的对象async with aiohttp.ClientSession() as s:#该函数内部的异步操作必须使用await进行修饰async with await s.get(url=url,headers=headers) as response:audio_data = await response.read() #read()返回的是二进制形式的响应数据return {'data':audio_data,'url':url}#任务对象的回调函数,进行数据的持久化存储 def saveData(task):dic_obj = task.result()name = dic_obj['url'].split('/')[-1]data = dic_obj['data']with open(name,'wb') as fp:fp.write(data)print(name+'下载完毕!')tasks = [] for url in urls:#调用该特殊函数,让其返回一个协程对象c = get_audio_data(url)#将协程对象封装到任务对象中task = asyncio.ensure_future(c)# 给任务对象绑定回调函数task.add_done_callback(saveData)#将任务对象添加到列表中tasks.append(task) #创建一个事件循环对象 loop = asyncio.get_event_loop() #将任务对象列表注册到事件循环对象中,并且开启事件循环 loop.run_until_complete(asyncio.wait(tasks))