美文网首页python
python多线程爬取

python多线程爬取

作者: 西歪A | 来源:发表于2017-09-18 20:57 被阅读0次

    科技在发展,时代在进步,我们的CPU也越来越快,CPU抱怨,P大点事儿占了我一定的时间,其实我同时干多个活都没问题的;于是,操作系统就进入了多任务时代。我们听着音乐吃着火锅的不在是梦想。
    python提供了两个模块来实现多线程thread 和threading ,thread 有一些缺点,在threading 得到了弥补,为了不浪费你和时间,所以我们直接学习threading 就可以了。

    1.代码:

    #coding: utf-8
    
    import threading
    import time
    
    class Foo(threading.Thread):
        def run(self):
            print  '我开始听音乐', '\n'
    
            time.sleep(1)
    
            print '我已经听完了音乐', '\n'
    
    def foo():
        print '我开始吃火锅', '\n'
    
        time.sleep(2)
    
        print '我已经吃完了火锅', '\n'
    
    t = Foo()
    d = threading.Thread(target = foo)
    t.start()
    # d.setDaemon(True)
    d.start()
    

    我们先听了一首音乐,通过Foo类来控制音乐的播放,音乐播放需要1秒钟,sleep()来控制音乐播放的时长。同时我们又看了一场电影,每一场电影需要2秒钟。输出如下图:

    1.png

    2. setDaemon(True):守护线程

    我们在标题1的代码里面进行一些修改来体现出setDaemon的作用

    #coding: utf-8
    
    import threading
    import time
    
    class Foo(threading.Thread):
        def run(self):
            print  '我开始听音乐', '\n'
    
            time.sleep(1)
    
            print '我已经听完了音乐', '\n'
    
    def foo():
        print '我开始吃火锅', '\n'
    
        time.sleep(2)
    
        print '我已经吃完了火锅', '\n'
    
    t = Foo()
    d = threading.Thread(target = foo)
    t.start()
    d.setDaemon(True)
    d.start()
    

    这里面d给设置了守护线程,2个线程同时开始,但是如果其他线程结束了,那么守护线程不管有没有完成都要结束。意思就是我们听着音乐,吃着火锅,音乐听完了,不管我们火锅有没有吃完,就得赶紧撤了。
    下图是输出结果:

    2.png

    3.join()阻塞

    继续修改我们的代码

    #coding: utf-8
    
    import threading
    import time
    
    class Foo(threading.Thread):
        def run(self):
            print  '我开始听音乐', '\n'
    
            time.sleep(1)
    
            print '我已经听完了音乐', '\n'
    
    def foo():
        print '我开始吃火锅', '\n'
    
        time.sleep(2)
    
        print '我已经吃完了火锅', '\n'
    
    t = Foo()
    d = threading.Thread(target = foo)
    t.start()
    d.setDaemon(True)
    d.start()
    d.join()
    

    我们将d进行阻塞,就是t执行完成,你才能执行d,也就是我们必须听完音乐,才能看电影 。
    下图是输出:

    3.png

    4.queue队列

    队列的意思是将东西放进去,可以随时取出来,下面有个代码演示:

    #coding: utf-8
    
    import Queue
    
    q = Queue.Queue()
    
    for i in range(5):
        q.put(i)
    
    while not q.empty():
        print q.get()
    

    输出结果:

    4.png

    这个应该很容易理解,就不用多解释。

    5.使用过多线程进行网页爬取

    代码如下:

    #!/usr/bin/env python
    # coding: utf-8
    
    #队列自己带锁,自己阻塞,线程安全的,不用自己显式的去编写
    import threading
    import requests
    import urllib
    import Queue
    from bs4 import BeautifulSoup
    
    
    class Foo(threading.Thread):
        def __init__(self, queue):
            threading.Thread.__init__(self)
            self.queue = queue
    
        def run(self):
            while True:
                key = self.queue.get()#取key列表里面的元素
                self.baidu(key)#运行函数baidu,传的参数是key
                self.queue.task_done()#取不到元素的时候自动退出程序
    
        def baidu(self, keys):
            gjc = urllib.quote(keys)
            url = 'https://www.baidu.com/s?ie=utf-8&mod=1&isid=C72D6237C6C55642&ie=utf-8&f=8&rsv_bp=0&rsv_idx=1&tn=baidu&wd=' + gjc + '&rsv_pq=816a886000062c06&rsv_t=c589dMGIygeVyzfmHXpCrPrWN2S4yWp8ttSNQ77uzbcec5H1cOVF2yiedcs&rqlang=cn&rsv_enter=1&rsv_sug3=7&rsv_sug1=3&rsv_sug7=100&rsv_sug2=0&inputT=89&rsv_sug4=41058&rsv_sid=1424_21119_17001_22072&_ss=1&clist=&hsug=&f4s=1&csor=2&_cr1=28095'
            header = {  'Refer':'https://www.baidu.com/',
                    'Host':'www.baidu.com',
                    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36',
                    'Cookie':'BAIDUID=75774ED511BF2DECB6C4A142D1F4CCDF:FG=1; PSTM=1499515257; BIDUPSID=DD4A04E2ED6C099A9F40EF86229306AB; FP_UID=616a141a155bdb648b37b61ff16cfdc3; BDRCVFR[yfg8b4Gp7xm]=yiTPYW-i3eTXZFWmv68mvqV; H_PS_645EC=9607I22jkjSFIiy14y6B2VlNt4TQHDqMpVs%2FNy6mdneG1NMGExHCLS0uRrs; BD_CK_SAM=1; PSINO=5; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BD_HOME=0; H_PS_PSSID=1447_19033_13550_21095_18560_17001_22160; BD_UPN=12314753'
                }
    
            content = requests.get(url, headers=header).content
            soup = BeautifulSoup(content, 'html.parser')
            keyword = soup.find_all('div', id="rs")
            for i in keyword:
                print i.get_text()
    
    if __name__ == '__main__':
        queue = Queue.Queue()
        key = ['苹果8', '小米Mx2', '华为mate10']
    
        for i in key:
            queue.put(i)#将key加入队列里面,用于run()的调用
    
        for item in range(3):
            t = Foo(queue)
            t.setDaemon(True)#守护进程,如果主线程结束,则会退出
            t.start()
    
        queue.join()#阻塞,函数baidu('苹果8')执行完成,才会继续进行下一次baidu('小米Mx2')
    

    输出结果:

    5.png

    6.python中多线程的优缺点

    多线程目前仅用于网络多线程采集, 以及性能测试。

    其它的语言也有类似的情况,线程本身的特点导致线程的适用范围是受限的。只有CPU过剩,而其它的任务很慢,此时用线程才是有益的,可以很好平衡等待时间,提高并发性能。线程的问题主要是线程的安全稳定性。线程无法强制中止,同时线程与主进程共享内存,可能会影响主进程的内存管理。在python里线程出问题,可能会导致主进程崩溃。 虽然python里的线程是操作系统的真实线程。那么怎么解决呢?通过我们用进程方式。子进程崩溃后,会完全的释放所有的内存和错误状态。所以进程更安全。 另外通过进程,python可以很好的绕过GIL,这个全局锁问题。但是进程也是有局限的。不要建立超过CPU总核数的进程,否则效率也不高。简单的总结一下。当我们想实现多任务处理时,首先要想到使用multiprocessing, 但是如果觉着进程太笨重,那么就要考虑使用线程。 如果多任务处理中需要处理的太多了,可以考虑多进程,每个进程再采用多线程。如果还处理不要,就要使用轮询模式,比如使用poll event, twisted等方式。如果是GUI方式,则要通过事件机制,或者是消息机制处理,GUI使用单线程。所以在python里线程不要盲目用, 也不要滥用。 但是线程不安全是事实。如果仅仅是做几个后台任务,则可以考虑使用守护线程做。如果需要做一些危险操作,可能会崩溃的,就用子进程去做。 如果需要高度稳定性,同时并发数又不高的服务。则强烈建议用多进程的multiprocessing模块实现。

    相关文章

      网友评论

        本文标题:python多线程爬取

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