python中的多线程爬虫

来源:互联网 发布:郑州景安网络怎么样 编辑:程序博客网 时间:2024/05/17 22:54

。,。本文是在学习网络爬虫课程期间写的,目的是为了总结所学的知识,内容都是笔者自己对多线程爬虫的理解,其中多有不足,希望不吝指教。

    多线程和单线程的区别在于线程的数量(字面意思理解),但多线程中各个线程之间数据空间和代码都是共享的,且每一个线程都有自己独立的寄存器。

   多线程的复杂性和优缺点也基本都是围绕着它数据空间共享的特点衍生出来的,对共享空间数据的安全性,数据操作的互斥性,容灾与文件死锁是多线程爬虫面临几个大问题。


死锁:在多线程爬虫里数据的传输和程序操作处处相关,很多程序只有得到指定的文件数据才能进行相应的操作,而对于多线程爬虫来讲出现双方都等待对方文件传输的情况,就被称为死锁。这时程序就无法获取到数据,就无法执行操作。比如A程序和B程序,A需要B发送的数据才能启动,而B需要A传输他的状态,这个时候就陷入了无限的等待,也就是死锁的问题


容灾:多线程之间共享数据空间,一旦其中一个线程出现问题其他的线程也会无法运行。


数据操作的互斥:由于数据共享,而本身对数据的操作就有互斥性,比如两个程序同时对一个数据进行读和写的操作,这是不可以的。

总的说来,多线程的复杂性在于它线程之间数据的共享,而多线程的优点也分成这几方面

1、数据空间共享,不需要搭建协议传输数据,这点相当方便,如果我们是创建多进程的程序,我们就需要考虑多个进程之间的通信和数据传输。


2、多线程可以提高cpu的效率。这里可以提一下python中的多线程,python作为编程语言是支持多线程操作的,但在python的底层有GIL全局锁,这应该开发者考虑到我们运行

程序冲突而设置的,它在一个程序运行时对全局加锁,让同一时间两个程序不能同时运行,造成我们对多核的利用能力有限,也决定了python程序的运行本质上是串联的形式。

但也不意味这python中多线程没有作用,在一般的情况下,我们的cpu是处在一个等待的状态下。计算机底层的数据传输时通过bus,而bus本身的传输能力有限,计算机在处理传输完数据后就进入等待的状态,所以我们完全可以将等待时间的cpu利用起来。


3、开发简单便捷,直接通过变量和内存就可以开发,对于爬虫来讲,多线程的方式可以加快我们爬取信息的速度。


python多线程的实现

>>> import time>>> import threading>>> threads=[]

首先,我们要引入time和多线程操作的threading模块,然后创建一个空的线程池threads=[ ]

def func1(a):    print(a)      print(ctime())        sleep(1)def func2(b):    print(b)    print(ctime())    sleep(1)
这里为了方便理解,定义了两个简单的函数func1和func2,实现一个简单的打印功能,为了看得更加清楚,使用了sleep()函数来让程序等待一秒时间方便我们观察。

func1("hello")func2("haha")
我们直接运行这两个测试函数

helloTue Aug 29 14:54:10 2017hahaTue Aug 29 14:54:11 2017
成功打印出来我们传入的参数和当前的时间,这里我们能够看到两个程序之间运行的时间间隔也刚好是sleep()函数设置的一秒钟。

t1 = threading.Thread(target=func1,args=("hello",))threads.append(t1)t2 = threading.Thread(target=func2,args=("haha",))threads.append(t2)

现在我们将func1和func2两个函数加入到前面创建的线程池中,这里的target代表的是线程运行的函数,args代表的是我们传入函数的参数。

def main():    for i in threads:        i.setDaemon(True)        i.start()
声明一个主函数main,使用for循环来遍历线程,这里的I.setDaemon(True)是为了防止程序被无限挂起,必须在start()方法之前定义。

>>> hellohahaTue Aug 29 15:09:00 2017Tue Aug 29 15:09:00 2017

运行主函数,我们可以看到,func1和func2两个函数被“同时”执行了,在显示的时间里都是15:09:00,这样我们就完成了一个简单的多线程。但这同时也引出了一个思考,如果我们想要子线程运行结束之后再做一些操作呢?

def main():    for i in threads:        i.setDaemon(True)        i.start()    print("主程序")
我们在main()函数for循环遍历线程池之后,加入一个print函数,来查看他的变化。

hellohaha主程序Tue Aug 29 15:12:26 2017Tue Aug 29 15:12:26 2017
运行结果如上,我们可以看到主程序在两个线程运行后直接运行了,这显然不是我们希望看到的。

def main():    for i in threads:        i.setDaemon(True)        i.start()    i.join()    print("主程序")main()
为了防止这种情况的出现,我们引入在主程序之前join方法,保证主程序不会在线程运行完毕之前开始。

hellohahaTue Aug 29 15:15:34 2017Tue Aug 29 15:15:34 2017主程序
结果如上,主程序在两个线程运行完毕后才执行了print函数。

全部代码:


from time import ctime,sleepimport threadingthreads=[]def func1(a):    print(a)      print(ctime())        sleep(1)def func2(b):    print(b)    print(ctime())    sleep(1)    t1 = threading.Thread(target=func1,args=('hello',))threads.append(t1)t2 = threading.Thread(target=func2,args=('haha',))threads.append(t2)def main():    for i in threads:        i.setDaemon(True)        i.start()    i.join()    print("主程序")main()