美文网首页
网络爬虫:多任务-协程

网络爬虫:多任务-协程

作者: 牛耀 | 来源:发表于2018-12-30 11:53 被阅读0次

    迭代器

    • 迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退
    1. 可迭代对象 我们已经知道可以对list、tuple、str等类型的数据使用for...in...的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代。

    2. 如何判断一个对象是否可以迭代

    from collections import Iterable
    
    print(isinstance([],Iterable)) --> True
    
    print(isinstance(1,Iterable)) -->False
    
    1. 可迭代对象进行迭代使用的过程,每迭代一次(即在for...in...中每循环一次)都会返回对象中的下一条数据,一直向后读取数据直到迭代了所有数据后结束。
    • 可迭代对象通过_iter方法向我们提供一个迭代器,我们在迭代一个可迭代对象的时候,实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器来依次获取对象中的每一个数据。一个具备了 _iter 方法的对象,就是一个 可迭代对象
    1. iter()函数与next()函数
    • list、tuple等都是可迭代对象,我们可以通过iter()函数获取这些可迭代对象的迭代器。然后我们可以对获取到的迭代器不断使用next()函数来获取下一条数据。iter()函数实际上就是调用了可迭代对象的_iter_方法。
    1. 如何判断一个对象是否是迭代器
    from collections import Iterator
    
    print(isinstance([1,2], Iterator)) -->False
    
    print(isinstance(iter([1,2]), Iterator)) -->True
    
    1. 迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。实际上,在使用next()函数的时候,调用的就是迭代器对象的next方法。所以,我们要想构造一个迭代器,就要实现它的next方法。并且python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现iter方法,迭代器的iter方法返回自身即可。
    • 一个实现了_iter_方法和next方法的对象,就是迭代器。

    生成器

    利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator)。生成器是一类特殊的迭代器。

    在使用生成器实现的方式中,我们将原本在迭代器_next_方法中实现的基本逻辑放到一个函数中来实现,但是将每次迭代返回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。

    使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)
    yield关键字有两点作用:
        保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
        将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
    可以使用next()函数让生成器从断点处继续执行.
    

    协程

    • 协程,又称微线程,纤程
      协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。 它自带CPU寄存器上下文。这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的。

    协程和线程差异

    在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住。

    • yeild简单实现
    import time
    
    def work1():
        while True:
            print("----work1---")
            yield
            time.sleep(0.5)
    
    def work2():
        while True:
            print("----work2---")
            yield
            time.sleep(0.5)
    
    def main():
        w1 = work1()
        w2 = work2()
        while True:
            next(w1)
            next(w2)
    
    if __name__ == "__main__":
        main()
    实质: 其实任务是在主线程中并发执行的,看上去像同时执行而已,当执行next()的时候,函数执行到yield的时候先暂停一下,然后之后再调用next()的时候,接着上一次暂停的位置执行
    

    实现协程

    • .greenlet的使用
    from greenlet import greenlet
    import requests
    
    def download1():
        print('正在下载1')
        #耗时的操作
        response = requests.get(url='https://github.com/')
        gre2.switch()
        print('download1下载完了')
        gre2.switch()
    
    
    def download2():
        print('正在下载2')
        response = requests.get(url='https://github.com/')
        gre1.switch()
        print('download2下载完了')
    
    
    gre1 = greenlet(download1)
    gre2 = greenlet(download2)
    gre1.switch()
    
    • greenlet已经实现了协程,但是这个还的人工切换,python还有一个比greenlet更强大的并且能够自动切换任务的模块gevent
    #gevent能够在内部自己实现携程之间的切换
    
    from gevent import monkey,pool
    import gevent,requests
    import lxml.etree as etree
    
    # 有耗时操作时需要
    monkey.patch_all()  # 将程序中用到的耗时操作的代码,换为gevent中自己实现的模块
    
    
    def download(url):
        print(url+'正在下载1')
        header = {'User-Agent':'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0'}
        response = requests.get(url,headers=header)
        print(len(response.text),url+'已完成1')
    
    def download2(url):
        print(url+'正在下载2')
        header = {'User-Agent':'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0'}
        response = requests.get(url,headers=header)
        print(len(response.text),url+'已完成2')
    
    pool = pool.Pool(2)
    
    gevent.joinall(
        [
            pool.spawn(download,'https://www.yahoo.com/'),
            pool.spawn(download,'https://www.taobao.com/'),
            pool.spawn(download,'https://github.com/'), 
            pool.spawn(download2,'https://www.yahoo.com/'),
            pool.spawn(download2,'https://www.taobao.com/'),
            pool.spawn(download2,'https://github.com/'), 
        ]
    )
    

    相关文章

      网友评论

          本文标题:网络爬虫:多任务-协程

          本文链接:https://www.haomeiwen.com/subject/pccvlqtx.html