• 小爬爬4.协程基本用法&&多任务异步协程爬虫示例(大数据量)


    1.测试学习

    (2)单线程:

    from time import sleep
    import time
    def request(url):
        print('正在请求:',url)
        sleep(2)
        print('下载成功:', url)
    urls = ['www.baidu.com','www.sogou.com','www.goubanjia.com']
    start = time.time()
    for url in urls:
        request(url)
    print(time.time()-start)

    测试结果:需要6秒多

    正在请求: www.baidu.com
    下载成功: www.baidu.com
    正在请求: www.sogou.com
    下载成功: www.sogou.com
    正在请求: www.goubanjia.com
    下载成功: www.goubanjia.com
    6.001747369766235

    (2)开启线程池:测试结果是2秒多

    from time import sleep
    import time
    from multiprocessing.dummy import Pool
    def request(url):
        print('正在请求:',url)
        sleep(2)
        print('下载成功:', url)
    urls = ['www.baidu.com','www.sogou.com','www.goubanjia.com']
    start = time.time()
    pool=Pool(3)
    pool.map(request,urls)
    print(time.time()-start)

    测试结果:

    正在请求: www.baidu.com
    正在请求: www.sogou.com
    正在请求: www.goubanjia.com
    下载成功: www.goubanjia.com
    下载成功: www.sogou.com
    下载成功: www.baidu.com
    2.034695625305176

    (3)在程序中是否可以一味的使用多线程,多进程?

    推荐:单线程+异步协程(效率最高,用的人不是很多,大量爬取数据是会用到的)

    下面了解一下

    协程(go和python独有的概念),,协程不会占用很高的内存

    领导在乎的是把数据爬取出来.

    主要还是request模块的学习.下面学习几个概念这几个概念,一会儿会在代码中有所体现

    event_loop:事件循环,相当于一个无限循环,我们可以把一些特殊函数注册(放置)到这个事件循环上,当满足某些条件的时候,函数就会被循环执行。程序是按照设定的顺序从头执行到尾,
    运行的次数也是完全按照设定。当在编写异步程序时,必然其中有部分程序的运行耗时是比较久的,需要先让出当前程序的控制权,让其在背后运行,让另一部分的程序先运行起来。当背后运行的程序完成后,
    也需要及时通知主程序已经完成任务可以进行下一步操作,但这个过程所需的时间是不确定的,需要主程序不断的监听状态,一旦收到了任务完成的消息,就开始进行下一步。loop就是这个持续不断的监视器。 coroutine:中文翻译叫协程,在 Python 中常指代为协程对象类型,我们可以将协程对象注册到事件循环中, 它会被事件循环调用。我们可以使用
    async 关键字来定义一个方法,这个方法在调用时不会立即被执行, 而是返回一个协程对象。 task:任务对象,它是对协程对象的进一步封装,包含了任务的各个状态。 future:任务对象,代表将来执行或还没有执行的任务,实际上和 task 没有本质区别。 另外我们还需要了解 async/await 关键字,它是从 Python 3.6 才出现的,专门用于定义协程。其中,async 定义一个协程,await 用来挂起阻塞方法的执行。

    (4)协程基础

    #asyncio是python3.6才出来的一种技术
    import asyncio
    async def requests(url):    #被async修饰,就会没有返回值,内部不会执行,只返回一个协程对象
        print('正在请求:',url)
        print('下载成功:',url)
    c=requests('www.baidu.com')
    print(c)

    得到下面的结果:

    <coroutine object requests at 0x000001A6E0150410>
    sys:1: RuntimeWarning: coroutine 'requests' was never awaited

    (5)升级初版

    import asyncio
    async def requests(url):  #async本质上是个生成器
        print('正在请求:',url)
        print('下载成功:',url)
    c=requests('www.baidu.com')
    
    #实例化一个事件循环对象
    loop=asyncio.get_event_loop()
    
    #将协程对象注册到时间循环对象中,并且我们需要启动事件循环对象
    loop.run_until_complete(c)    #可以无限循环的位置(c...)

    得到下面的结果:

    正在请求: www.baidu.com
    下载成功: www.baidu.com

    (6)

    import asyncio
    async def requests(url):  #async本质上是个生成器,特殊函数async关键字
        print('正在请求:',url)
        print('下载成功:',url)
    c=requests('www.baidu.com')      #协程
    
    #实例化一个事件循环对象
    loop=asyncio.get_event_loop()
    
    #添加的新点,任务对象
    #方法1:创建一个任务对象,将协程对象封装到了该对象中
    task=loop.create_task(c)  #这一步是进一步的处理,.   #任务对象
    #方法2:另一种形式实例化任务对象的方法,不需要上边的实例化事件循环对象
    # task=asyncio.ensure_future(c)
    print(task)   #pending
    #将协程对象注册到事件循环对象中,并且我们需要启动事件循环对象
    loop.run_until_complete(task)  #括号里边可以有多个参数,无限循环  #事件循环
    print(task)     #finished
    #核心:绑定回调

    注意:任务对象就是对协程的一种封装

    结果:

    <Task pending coro=<requests() running at F:/Python_workspace_S18/papa_part/day4/2.协程基础.py:49>>
    正在请求: www.baidu.com
    下载成功: www.baidu.com
    <Task finished coro=<requests() done, defined at F:/Python_workspace_S18/papa_part/day4/2.协程基础.py:49> result=None>

    (7)给任务对象绑定回调,这个虽然简单但是及其重要

    单任务异步协程

    #单任务异步协程
    import asyncio
    
    async def request(url):
        print('正在请求:', url)
        print('下载成功:', url)
        return url
    
    #回调函数必须有一个参数:task
    #task.result():任务对象中封装的协程对象对应的特殊函数内部的返回值
    def callbak(task):                  #必须有一个参数,并且必须是任务本身
        print('the callback')
        print(task.result())      #result就是上边函数的url
    c = request('www.baidu.com')
    
    loop=asyncio.get_event_loop()
    
    #给任务对象绑定一个回调函数
    task=asyncio.ensure_future(c)      #先执行这一步,才能执行回调
    task.add_done_callback(callbak)    #这个多了一个回调函数
    #什么是回调函数?当任务对象执行完成之后,可以回头调用给其绑定的另外一个函数
    loop.run_until_complete(task)       #全部执行完成之后,才执行回调函数

    结果:

    正在请求: www.baidu.com
    下载成功: www.baidu.com
    the callback
    www.baidu.com

    另一种写法:

    #单任务异步协程
    import asyncio
    
    async def request(url):
        print('正在请求:', url)
        print('下载成功:', url)
        return url
    
    #回调函数必须有一个参数:task
    #task.result():任务对象中封装的协程对象对应的特殊函数内部的返回值
    def callbak(task):                  #必须有一个参数,并且必须是任务本身
        print('the callback')
        print(task.result())      #result就是上边函数的url
    c = request('www.baidu.com')
    
    loop=asyncio.get_event_loop()
    
    #给任务对象绑定一个回调函数
    # task=asyncio.ensure_future(c)      #先执行这一步,才能执行回调   #第一种写法
    task=loop.create_task(c)                                        #第二种写法
    task.add_done_callback(callbak)    #这个多了一个回调函数
    #什么是回调函数?当任务对象执行完成之后,可以回头调用给其绑定的另外一个函数
    loop.run_until_complete(task)       #全部执行完成之后,才执行回调函数

    回调函数的目的:对数据进行解析

    (8)多任务异步协程:(1个任务不是异步),

    只有多任务异步协程才是有意义的

    asyncio出现来了async和await

    from time import sleep
    import asyncio
    import time
    urls = ['www.baidu.com','www.sogou.com','www.goubanjia.com']
    start = time.time()
    async def request(url):
        print('正在请求:',url)
        #在多任务异步协程实现中,不可以出现不支持异步的相关代码。比如time模块中的sleep
        # sleep(2)
        await asyncio.sleep(2)    #可以用asyncio里边的sleep,才能计算出异步时间,支持异步,并且必须加上await才能等待
        print('下载成功:',url)
    
    loop = asyncio.get_event_loop()         #通过事件循环执行
    #任务列表:放置多个任务对象
    tasks = []
    for url in urls:
        c = request(url)
        task = asyncio.ensure_future(c)
        tasks.append(task)                      #依次追加到列表中
    
    loop.run_until_complete(asyncio.wait(tasks))
    #作用:将任务对象注册到事件当中,只能启动一次的用法
    #里边的参数代表的是,多个事件循环必须是异步的因此需要加上asyncio.wait(),有阻塞就挂起,执行完再搞阻塞
    
    print(time.time()-start)

    得到下面的结果:

    正在请求: www.baidu.com
    正在请求: www.sogou.com
    正在请求: www.goubanjia.com
    下载成功: www.baidu.com
    下载成功: www.sogou.com
    下载成功: www.goubanjia.com
    2.040722608566284

    (9)多任务异步协程在flask中的应用

    下面是flaskServer.py文件,首先需要安装flask模块

    # Author: studybrother sun
    from flask import Flask
    import time
    
    app = Flask(__name__)
    
    @app.route('/bobo')
    def index_bobo():
        time.sleep(2)    #耗时两秒
        return 'Hello bobo'
    
    @app.route('/jay')
    def index_jay():
        time.sleep(2)
        return 'Hello jay'
    
    @app.route('/tom')
    def index_tom():
        time.sleep(2)
        return 'Hello tom'
    
    if __name__ == '__main__':
        app.run(threaded=True)

     启动:服务器上的程序,访问下面的网址,需要等待2s钟才能得到下面的结果

    上边的服务器启动之后,我们再次启动下面的程序

    import requests    #目的是发送请求
    import asyncio
    import time
    #单线程+多任务异步协程
    urls = [
        'http://127.0.0.1:5000/jay',
        'http://127.0.0.1:5000/bobo',
        'http://127.0.0.1:5000/tom'
    ]
    start=time.time()
    for url in urls:
        page_text=requests.get(url=url).text   #请求url得到的返回值
        print(page_text)
    print(time.time()-start)

    得到下面的结果:

    Hello jay
    Hello bobo
    Hello tom
    6.132113456726074

    下面是单线程+异步协程在爬虫中的用法:

    import requests
    import asyncio
    import time
    #单线程+多任务异步协程
    urls = [
        'http://127.0.0.1:5000/jay',
        'http://127.0.0.1:5000/bobo',
        'http://127.0.0.1:5000/tom'
    ]
    async def get_pageText(url):
        print('正在下载:',url)
        #requests模块是不支持异步操作的。,下面是6s的原因,不支持异步
        page_text = requests.get(url).text
        print('下载完毕:',url)
    
        return page_text
    
    start = time.time()
    tasks = []
    for url in urls:
        c = get_pageText(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)

    得到下面的结果:

    正在下载: http://127.0.0.1:5000/jay
    下载完毕: http://127.0.0.1:5000/jay
    正在下载: http://127.0.0.1:5000/bobo
    下载完毕: http://127.0.0.1:5000/bobo
    正在下载: http://127.0.0.1:5000/tom
    下载完毕: http://127.0.0.1:5000/tom
    6.1820220947265625

    应该是2s多,怎么会这样????

    #aiohttp:支持异步的一个基于网络请求的模块
    # pip install aiohttp     #安装这个异步请求模块
    
    import requests
    import asyncio
    import time
    import aiohttp        #支持异步的网络请求内容,升级支持异步请求的requests模块
    #单线程+多任务异步协程
    urls = [
        'http://127.0.0.1:5000/jay',
        'http://127.0.0.1:5000/bobo',
        'http://127.0.0.1:5000/tom'
    ]
    #代理操作:
    #async with await s.get(url,proxy="http://ip:port") as response:
    async def get_pageText(url):
       async with aiohttp.ClientSession() as s:         #实例化请求对象
          async with await s.get(url) as response:      #得到相应对象,在异步中有with就需要加上async
               #发起请求也是需要加上await的
               page_text = await response.text()          #注意这个地方text是个方法,得到响应数据就需要先挂起
                # 借助于回调函数进行响应数据的解析操作await
               print(page_text)
               return page_text
    #封装回调函数用于数据解析
    
    
    start = time.time()
    tasks = []
    for url in urls:
        c = get_pageText(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)

    已经实现.得到下面的结果:

    Hello jay
    Hello bobo
    Hello tom
    2.1180801391601562

    升级:

    #aiohttp:支持异步的一个基于网络请求的模块
    # pip install aiohttp
    
    import requests
    import asyncio
    import time
    import aiohttp        #支持异步的网络请求内容,升级支持异步请求的requests模块
    #单线程+多任务异步协程
    urls = [
        'http://127.0.0.1:5000/jay',
        'http://127.0.0.1:5000/bobo',
        'http://127.0.0.1:5000/tom'
    ]
    #代理操作:写代理时候的区别
    #async with await s.get(url,proxy="http://ip:port") as response:
    async def get_pageText(url):
       async with aiohttp.ClientSession() as s:         #实例化请求对象
          async with await s.get(url) as response:      #得到相应对象,在异步中有with就需要加上async
               #发起请求也是需要加上await的
               page_text = await response.text()          #注意这个地方text是个方法,得到响应数据就需要先挂起
                # 借助于回调函数进行响应数据的解析操作await
               print(page_text)
               return page_text
    #封装回调函数用于数据解析
    def parse(task):               #回调函数
        #1.获取响应数据
        page_text = task.result()
        print(page_text+',即将进行数据解析!!!')
        #解析操作写在该位置
    
    start = time.time()
    tasks = []
    for url in urls:
        c = get_pageText(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)

    下面得到的结果:顺序jay>bobo>tom>

      注意:这个是有一定顺序的

    Hello jay
    Hello jay,即将进行数据解析!!!
    Hello bobo
    Hello bobo,即将进行数据解析!!!
    Hello tom
    Hello tom,即将进行数据解析!!!
    2.0991756916046143

    get里边是paras,post里边是data,这个参数注意一下,还有代理还是有区别的,复数变成了单数

     音频,视频,占用的视频比较大,可以用这个,一般是不用的.

  • 相关阅读:
    .net 5.0
    多线程synchronized锁
    多线程(Thread、线程创建、线程池)
    电商秒杀方法
    sb @EnableAsync与@Async 20210310
    spring boot @EnableAsync 异步调用
    五代十国军事人物
    唐朝末年,七大割据军阀势力
    盘点万历之后,镇守辽东的8位军事统帅,堪称有军事作为的仅三人
    Cookie-Session or JWT
  • 原文地址:https://www.cnblogs.com/studybrother/p/10951229.html
Copyright © 2020-2023  润新知