Python - 06 - 协程

89 阅读2分钟

概念

  • 协程能够更加高效的利用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')
  • 然后就可以继续愉快的爬虫了