爬虫的单线程+多任务异步协程:asyncio 3.6
单线程+多任务异步协程:asyncio 3.6
-
事件循环
- 无限循环的对象.事件循环中最终需要将一些 特殊的函数(被async关键字修饰的函数) 注册在该对象中.
-
协程
- 本质上是一个对象.可以把协程对象(特殊的函数)注册到事件循环中
-
任务对象
- 就是对协程对象进一步的封装.
-
绑定回调: task.add_done_callback(func)
- func(task):task参数表示的就是绑定的任务对象
- task.result():返回就是任务对象对应的特殊函数内部的返回值
- 回调多被用作于爬虫中的解析方法
-
await
- 在任务对象对应的特殊函数内部的实现语句中,如果出现了阻塞的操作,则必须使用await进行修饰
-
异步操作的体现
- 当将多个协程对象(特殊的函数)注册到事件循环中后,事件循环开启后,则会循环执行其内部的协程对象们
- 假如事件循环对象在执行某一个协程对象时,发生了阻塞,则事件循环对象不会等待阻塞结束,反而会执行下一个协程对象
-
aiohttp:支持异步的网络请求模块
- 中文文档
- 环境安装 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_text async 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))