前言

前面我们讲到了基本爬虫包括请求、提取和保存三个部分,这是一个基本爬虫应该有的部分,那么这时候的这个爬虫有了能爬能存的能力,但是这种能力是很弱的,弱主要体现在三点:①爬虫本身健壮性并不高,有很多情况不一定考虑到;②爬虫爬的很慢,效率很低;③防反爬能力不强,容易被Ban掉

我们在这篇博文就会就上面第二点讲讲怎么通过运用多线程和多进程来提高爬虫的工作效率.

多线程

待补充

多进程

首先要声明一点,多进程和多线程是两个不同的概念,别混淆了。

进程的调度:当有多个进程并行执行时,每个进程得到的时间片的时间不一样,哪个进程接受哪个请求以及执行完成的时间都是不定的,所以不处理的话会输出乱序。

python中多进程用到的模块是multiprocessing(python就是模块多…一些基本的,常用的都已经有模块了……),multiprocessing[下面简称mp]的很大的一部分与threading模块使用同一套API

只有Linux的fork()才是真正意义上的多进程,所以在Linux上就用multiprocessing库就好了

mp在多核CPU中的表现会比threading好很多,因为可以做到跑满多核。

如果操作的进程数目不大(十几左右)尚可用multiprocessing.Process类动态生成多个进程,但是如果数目很大(上百),手动限制进程数目就显得特别繁琐,这时候进程池就派上用场了。

进程池:Pool【感觉已经是mp的高级用法了】,可以提供指定数量的进程供用户调用,每当有请求提交到Pool的时候,如果进程池还没有满,就会创建一个新的进程来执行请求,如果进程满了,则请求将会被告知先等待,直至进程池中有进程结束才会放入,以执行请求。

简单来说,就是进程池可创建多个进程,这些进程就像是随时待命的士兵,准备执行任务。

1.在Unix平台上,当某个进程终结以后,该进程需要被其父进程调用wait,否则将会成为Zombie Process.所以有必要对每一个进程调用join()方法(实际上等同于wait),而对于多线程而言,由于只有一个进程,故不存在此必要性。

2.mp包提供了threading包中没有的IPC=Inter-Process Communication——进程间通信[比如Pipe和Queue],效率上会更高,应该优先考虑Pipe和Queue(Pipe和Queue都是FIFO,但是Queue允许多进程放入,多进程从队列中取出对象),避免使用Lock/Event/Semaphore/Condition等同步方式。【因为它们占据的不是用户进程资源】

3.多进程应避免共享资源,多线程中,我们可以比较容易地共享资源,比如使用全局变量或者传参,在多进程的情况下,由于每个进程有自己独立的内存空间,以上方法并不合适,此时可以用共享内存和Manager的方法来共享资源

python使用mp包中的进程池主要就是下面这几句话:

pool=Pool(n)#建立进程池,n就是代表建立n个进程,这个n的设定一般与CPU的核数一样  
pool.map(def,list)#把列表list里面的每一项映射到你所定义的def函数内,有点通过这句话做list各项循环的意味  
pool.close()#关闭进程池,不在接受新的进程——Pool等父  
pool.join()#主进程阻塞等待子进程退出(安全退出),父子进程同步.——父等子  

没错,用起来的都是套路,其实你完全可以直接复制粘贴,谨慎起见,我还是稍微来解释一下为什么要这么写:

1.对Pool对象调用join()方法会等待所有进程执行完
2.调用join()之前必须先调用close()方法,让其不再接受新的Process
3.pool.clpse()和pool.terminate()的区别是:close()会等到父进程结束后,再关闭Pool.而terminate()则直接关闭Pool

多线程和多进程的区别

1.多进程会把同一个变量在每个进程中拷贝一份,互相不影响。
2.多线程每个变量都是由所有线程共享,因此,多线程最大的危险在于多个线程同时修改一个变量,容易把内容改乱了,这就是竞争【竞争的解决关键是要确定先后顺序】——你们可以一起跑,但是要一个个出来.

<未完待续>