Python中多进程在爬虫中的使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_22594309/article/details/53727084

本来这周准备写一个整合ip池,多进程的高效爬取所有职位详细信息的爬虫的,结果在多进程这一块儿折腾了好久,简直把我气死,虽然内容其实不多,但把自己学习的过程写下来,希望能帮到有同样困惑的朋友。

我参照的是廖雪峰老师写的一个Python教程,有兴趣大家可以百度一下,我觉得还是挺适合像我这样的新手的。

OK,话不多说,在上一篇介绍IP池的使用的文章中,最后我也提到了,通过打开网站的方式验证IP可用性的方法执行的很慢,所以必须想办法加快这个验证的速度,最容易想到的就是采用多任务的方式。

实现多任务最常见的两种方式就是多进程和多线程了。多进程,顾名思义,多个进程并发执行,可以有效提高程序的执行效率,优点是非常稳定,即使有子进程崩溃了,主进程和其他进程依然可以继续执行,但缺点是在windows下创建进程的开销比较大,而且如果进程太多,往往会影响整个系统的调度。而多线程是指一个进程内多个线程同时执行,进而提高程序执行效率,其优点可能是比多进程稍微快一点,但缺点也很明显,多线程中一个线程出现了问题就会导致整个进程崩溃,因此稳定性不是很高。

然后由于Python解释器在执行代码是有一个GIL锁(Global Interpreter Lock),它的作用是任何Python进程执行前都必须先获得GIL锁,然后没执行100条字节码释放GIL锁,让别的进程有机会执行,因此多线程在Python中只能交替执行,所以针对改善我们的爬虫程序,我们选择使用多进程的方法。

在windows系统下,Python可以通过multiprocessing库实现多进程,先从最简单地开启一个子进程开始

from multiprocessing import Process
import os

def run_proc(name):
    print 'Run child process %s (%s) ' %(name,os.getpid())
    
if __name__=='__main__':
    print 'Parent process %s ' %os.getpid()
    p=Process(target=run_proc,args=('test',))
    print 'Process will start'
    p.start()
    p.join()
    print 'Process end'
在上面的代码中,我们定义了一个函数用于输出当前进程的id,然后利用multiprocessing中的Process函数新建一个进程,start()函数用于启动进程,join()函数用于子进程执行完后再向下执行,然而结果并不如预料那般


子进程似乎没有执行,在这个地方我花费了很长时间,一直在想哪儿有问题,最后证明代码确实没问题,问题在于多进程这个操作在IDE里似乎无法执行,所以我又换到命令行里执行以下。这里提示一下,win7或者问win10在文件夹里,按住shift邮件就会出现打开命令行,python yourfile.py 即可执行,上述程序结果如下:


我们可以看到成功地打开了一个新进程,好的,第一步完成了,那下一步是如何同时打开多个进程呢?别急,multiprocessing模块中还提供了Pool对象,可以同时开启多个进程,一个简单的测试代码如下

from multiprocessing import Pool
import time
import os


def task(name):
    print 'Run task %s (%s)...'%(name,os.getpid())
    print time.time()
    time.sleep(3)
    
if __name__=='__main__':
    print 'Parent process %s'%os.getpid()
    p=Pool()
    for i in range(9):
        p.apply_async(task,args=(i,))
    print 'Waiting for all subprocess done ...'
    p.close()
    p.join()
    print 'All subprocess done'
在这里我们定义了一个task函数,用于输出当前进程的id和当前进程开始的时间,然后创建了一个Pool对象,它可以引入processes参数来指定同时启动进程的数量,默认的是你机器的核心数。join()函数用于等待所有子进程执行完毕,而join()函数之前必须引用close()函数来禁止新的进程的创建,运行结果如下

我们可以看到前面八个进程几乎是同时开始的,而最后一个进程是在有一个进程结束之后(等待3s)开始的,因为我机器的核心数是8,所以这很合乎常理。到这里,我们对Python中多进程的使用已经有一定的认识了,下一步就是应用到我们的ip验证当中了,其实方法和我们的测试程序一模一样,我们把之前获取可用IP地址的函数拆分成一个获取所有代理的函数和一个验证函数,方便我们进行多进程的操作,通过单进程执行和多进程执行,我们比较多进程方法引入带来效率的提高,具体实现代码如下

# -*- coding: utf-8 -*-
"""
__author__='CD'
"""

import requests
from bs4 import BeautifulSoup
import csv
import time
from multiprocessing import Pool

def getIp(numpage):
    csvfile = file('ips.csv', 'wb')  
    writer = csv.writer(csvfile)
    url='http://www.xicidaili.com/nn/'
    user_agent='IP'
    headers={'User-agent':user_agent}
    for i in xrange(1,numpage+1):
        real_url=url+str(i)
        response=requests.get(real_url,headers=headers)
        content=response.text
        bs=BeautifulSoup(content)
        trs=bs.find_all('tr')
        for items in trs:
            tds=items.find_all('td')
            temp=[]
            try:
                temp.append(tds[1].text)
                temp.append(tds[2].text)
                writer.writerow(temp)
            except:
                pass                
getIp(1)            

def getProxy():
    reader=csv.reader(open('ips.csv'))
    Proxy=[]
    for row in reader:
        proxy={"http":row[0]+':'+row[1]}
        Proxy.append(proxy)
    return Proxy
        
        
def test(proxy):
   try:
       response=requests.get('http://www.baidu.com',proxies=proxy,timeout=2)
       if response:
           return proxy
   except:
       pass




if __name__=='__main__':
    proxy=getProxy()
    IPPool1=[]
    time1=time.time()
    for item in proxy:
        IPPool1.append(test(item))
    time2=time.time()
    print 'singleprocess needs '+str(time2-time1)+' s'
    pool=Pool()
    IPPool2=[]
    temp=[]
    time3=time.time()
    for item in proxy:
        temp.append(pool.apply_async(test,args=(item,)))
    pool.close()
    pool.join()
    for item in temp:
        IPPool2.append(item.get())
    time4=time.time()
    print 'multiprocess needs '+str(time4-time3)+' s'
在这个程序里,我们只爬取了第一页的ip并进行了验证结果如下


可以看到,采用多进程之后,验证的时间缩短了8倍,这是因为我采用了默认的设置,同一时间的进程数为我机器的核心数是8,如果将进程数进一步提高可以获得更快的速度,当然速度的减小并不是线性的,肯定后面效果越来越差,因为进程的切换需要时间,多进程也增加了系统调度的难度等等。

好了,利用Python的多进程来减小代理ip验证所需时间的技能get到了,当然完全可以举一反三,因为爬虫本来就是个IO密集型的工作,所以我们在爬取网页时同样也可以使用多进程来减小等待时间,哈哈哈,真开心~




阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页