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

来源:互联网 发布:淘宝网上禁止出售 编辑:程序博客网 时间:2024/06/14 21:39

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

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

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

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

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

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

from multiprocessing import Processimport osdef 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 Poolimport timeimport osdef 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 requestsfrom bs4 import BeautifulSoupimport csvimport timefrom multiprocessing import Pooldef 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:       passif __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密集型的工作,所以我们在爬取网页时同样也可以使用多进程来减小等待时间,哈哈哈,真开心~




0 0