单线程+多任务异步协程完成爬虫任务

### - 单线程+多任务异步协程

异步进程主要是又三点构成:特殊函数所谓的特殊函数就是被async关键字所修饰的函数,这个函数的返回值由回调函数拿到,协成创建指定的协成对象,实例化任务对象

- 特殊的函数

- 被async关键字修饰的函数定义,该函数就是一个特殊的函数
- 特殊之处:
  - 特殊函数被调用后,函数定义的内部实现语句没有被立即执行
  - 该函数会返回一个协程对象
  - 特殊函数 == 一组指定形式的操作 == 协程对象
    - 协程对象 == 一组指定形式的操作

- 协程

- 创建方式:通过特殊函数调用返回
- 协程对象 == 一组指定形式的操作

- 任务对象

- 任务对象就是一个高级的协程对象

- 任务对象 == 协程对象 == 一组指定形式的操作

  - 任务对象 == 一组指定形式的操作

- 任务对象的创建:

  - ```
    asyncio.ensure_future(c)#这个参数c是实例化的协成对象
    ```

- 给任务对象绑定回调函数:

#定义回调函数
def parse(t):#必须且只能有一个参数(回调函数的调用者表示的那一个任务对象)
    result = t.result() #result()返回的就是参数t表示的任务对象对应的特殊函数中的返回值
    print(result)
    
 task.add_done_callback(parse) #给任务对象绑定回调函数

- 事件循环对象

- 作用:充当一个容器,用来装在任务对象。当事件循环启动后,就可以异步的执行其内部装载的一个或多个任务对象。
- 创建:loop = asyncio.get_event_loop()
- 启动加载:loop.run_until_complete(task)

- 完整实现:

  • import asyncio
    import time
    #特殊函数的定义
    async def get_request(url):
        print('正在请求url:',url)
        time.sleep(2)
        print('请求结束:',url)
        return 123
    #定义回调函数
    def parse(t):#必须且只能有一个参数(回调函数的调用者表示的那一个任务对象)
        result = t.result() #result()返回的就是参数t表示的任务对象对应的特殊函数中的返回值
        print(result)
    #协程对象
    c = get_request('www.1.com')
    #任务对象
    task = asyncio.ensure_future(c)
    task.add_done_callback(parse) #给任务对象绑定回调函数
    #事件循环对象
    loop = asyncio.get_event_loop() #创建一个事件循环对象
    loop.run_until_complete(task) #将一个任务对象装载在loop对象中,切启动了时间循环对象
    
    

- 多任务的异步效果的体现

  • wait()方法:可以将tasks列表中的每一个任务对象赋予可被挂起的权限。
    • 挂起:让当前的任务对象交出cpu的使用权。
  • 注意:在特殊函数的实现内部,不可以出现不支持异步模块的代码,否则会中断整个异步效果!
  • await关键字:保证阻塞操作一定会被执行!、
import asyncio
import time

start = time.time()
urls = [
    'www.1.com',
    'www.2.com',
    'www.3.com'
]
#特殊函数的定义
async def get_request(url):
    print('正在请求url:',url)
    await asyncio.sleep(2)
    print('请求结束:',url)
    return 123
tasks = []
for url in urls:
    c = get_request(url)
    task = asyncio.ensure_future(c)
    tasks.append(task)

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

print('总耗时:',time.time()-start)

- 爬取自己服务器里的数据,实现异步效果 Server.py

     from flask import Flask,render_template
     from time import sleep
     app = Flask(__name__)
     
     @app.route('/bobo')
     def index1():
     	sleep(2)
     	return render_template('test.html')
     @app.route('/jay')
     def index2():
     	sleep(2)
     	return render_template('test.html')
     @app.route('/tom')
     def index3():
     	sleep(2)
     	return render_template('test.html')
     if __name__ == '__main__':
         app.run(debug=True)

爬虫端的代码:

import asyncio
import requests
import time
from lxml import etree
start = time.time()
urls = ['http://127.0.0.1:5000/bobo',
       'http://127.0.0.1:5000/jay',
       'http://127.0.0.1:5000/tom',]

async def get_request(url):
    response = requests.get(url)
    page_text = response.text
    return page_text

def parse(task):
    page_text = task.result()
    tree = etree.HTML(page_text)
    data = tree.xpath('//body/text()')[0]
    print(data)

tasks = []
for url in urls:
    c = get_request(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)


  #####上述没有实现异步效果,原因在于requests模块不支持异步。

  #####使用aiohttp来代替requests

  #####aiohttp:

pip install aiohttp

 ###编码流程:编写大致架构

 ```python
async def get_request(url):
    with aiohttp.ClientSession() as sess:#创建了一个请求对象
        with sess.get(url) as response: #发起请求,返回响应对象
            page_text = response.text() #read()返回byte类型
            return page_text

补充细节

  • 在每个with前加上async关键字
    在每一步阻塞前加上await关键字
            async def get_request(url):
                async with aiohttp.ClientSession() as sess:#创建了一个请求对象
                    async with await sess.get(url) as response: #发起请求,返回响应对象
                        page_text = await response.text() #read()返回byte类型
                        return page_text
         

完整操作

  import asyncio
  import aiohttp
  import time
  from lxml import etree
  start = time.time()
  urls = ['http://127.0.0.1:5000/bobo',
         'http://127.0.0.1:5000/jay',
         'http://127.0.0.1:5000/tom',]
  
  async def get_request(url):
      async with aiohttp.ClientSession() as sess:#创建了一个请求对象
          async with await sess.get(url) as response: #发起请求,返回响应对象
              page_text = await response.text() #read()返回byte类型
              return page_text
  
  def parse(task):
      page_text = task.result()
      tree = etree.HTML(page_text)
      data = tree.xpath('//body/text()')[0]
      print(data)
  
  tasks = []
  for url in urls:
      c = get_request(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)   
          

posted @ 2021-08-17 21:32  Fleeting__Time  阅读(86)  评论(0编辑  收藏  举报