zoukankan      html  css  js  c++  java
  • 小爬爬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,这个参数注意一下,还有代理还是有区别的,复数变成了单数

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

  • 相关阅读:
    POJ 2018 二分
    873. Length of Longest Fibonacci Subsequence
    847. Shortest Path Visiting All Nodes
    838. Push Dominoes
    813. Largest Sum of Averages
    801. Minimum Swaps To Make Sequences Increasing
    790. Domino and Tromino Tiling
    764. Largest Plus Sign
    Weekly Contest 128
    746. Min Cost Climbing Stairs
  • 原文地址:https://www.cnblogs.com/studybrother/p/10951229.html
Copyright © 2011-2022 走看看