1. 程式人生 > >Python中多程序在爬蟲中的使用

Python中多程序在爬蟲中的使用

本來這周準備寫一個整合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密集型的工作,所以我們在爬取網頁時同樣也可以使用多程序來減小等待時間,哈哈哈,真開心~