zoukankan      html  css  js  c++  java
  • 爬虫的单线程+多任务异步协程:asyncio 3.6

    单线程+多任务异步协程:asyncio 3.6

    • 事件循环

      • 无限循环的对象.事件循环中最终需要将一些 特殊的函数(被async关键字修饰的函数) 注册在该对象中.
    • 协程

      • 本质上是一个对象.可以把协程对象(特殊的函数)注册到事件循环中
    • 任务对象

      • 就是对协程对象进一步的封装.
    • 绑定回调: task.add_done_callback(func)

      • func(task):task参数表示的就是绑定的任务对象
      • task.result():返回就是任务对象对应的特殊函数内部的返回值
      • 回调多被用作于爬虫中的解析方法
    • await

      • 在任务对象对应的特殊函数内部的实现语句中,如果出现了阻塞的操作,则必须使用await进行修饰
    • 异步操作的体现

      • 当将多个协程对象(特殊的函数)注册到事件循环中后,事件循环开启后,则会循环执行其内部的协程对象们
      • 假如事件循环对象在执行某一个协程对象时,发生了阻塞,则事件循环对象不会等待阻塞结束,反而会执行下一个协程对象
    • aiohttp:支持异步的网络请求模块

    • 简单示例

      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))
      
  • 相关阅读:
    C# 串口调试助手源码
    C# 中 textBox 侧面滑条 属性
    C# 中 comboBox 禁止输入
    VS2015 下载 破解
    中国移动OnetNet云平台 GET指令使用
    JSON JsonArray和JsonObject学习资料
    sublime 添加 ConvertToUTF-8
    sublime 添加 颜色插件 colorcoder
    线程池
    爬虫中基本的多线程
  • 原文地址:https://www.cnblogs.com/Godisgirl/p/11025195.html
Copyright © 2011-2022 走看看