Python實現網絡爬蟲

我使用的網絡爬蟲下載網頁的算法是廣度搜索(BFS),網絡上對爬蟲實現算法的評價中,廣度搜索的算法是排行第二的,最好的算法是按網頁重要性排序後再肯定下載順序(這個算法很靈活,怎麼排序本人不是很瞭解)。

進入正題,描述如何實現:
    拿到一個已經有了描述的辦法,實現它能夠按自頂向下的思路,先將大的步驟描述出來,而後分割成小的問題,一部分一部分地解決。
    對於一個網絡爬蟲,若是要按廣度遍歷的方式下載,它就是這樣幹活的:
        1.從給定的入口網址把第一個網頁下載下來
        2.從第一個網頁中提取出全部新的網頁地址,放入下載列表中
        3.按下載列表中的地址,下載全部新的網頁
        4.從全部新的網頁中找出沒有下載過的網頁地址,更新下載列表
        5.重複三、4兩步,直到更新後的下載列表爲空表時中止
    其實就是簡化成下面的步驟:
        1.按下載列表進行下載
        2.更新下載列表
        3.循環操做1,2,直到列表爲空結束

    因此最初的設想就是寫一個函數裏面幹這個:
def craw():
    while len(urlList) != 0
        Init_url_list()
        Download_list()
        Update_list()
     固然,上面這個函數是工做不起來的,它只是最頂層的一個想法,底層的實現還沒作。不過這一步很重要,至少讓本身知道該幹什麼了。
    下面的事情就是將函數每一部分實現,這個能夠放在一個類裏去實現,我把它命名爲WebCrawler。
     在python裏,要按一個地址下載一個網頁那並非什麼難事,你能夠用urllib裏的urlopen去鏈接上某一個網頁,而後調用獲取到的對象的read方法,能夠獲得網頁的內容的字符串,像這樣:
IDLE 2.6.6      ==== No Subprocess ====
>>> import urllib
>>> f = urllib.urlopen('http://www.hfut.edu.cn')
>>> s = f.read()
>>>
     這樣上面變量 s 裏面存的就是從http://www.hfut.edu.cn這個地址裏獲取到的網頁的內容了,是str數據類型。下面你要怎麼用均可以了,把寫入文件或從中提取新的地址就隨你意了。固然,只要寫入文件,就算下載完了這個頁面。
     一個爬蟲程序下載的速度確定是很重要的問題,誰也不想用一個單線程的爬蟲用一次只下一個網頁速度去下載,我在學校校園網,測試了單線程的爬蟲,平均每秒才 下1k。因此解決的辦法只有用多線程,多開幾個鏈接同時下載就快了。本人是Python新手,東西都是臨時拿來用的。
     下載線程我是用了另一個類,命名爲CrawlerThread,它繼承了threading.Thread這個類。
     由於涉及到更新下載列表的問題,線程對某個表的讀寫還要考慮同步,我在代碼裏使用了線程鎖,這個用threading.Lock()構造對象。調用對象的 acquire()和release()保證每次只有一個線程對錶進行操做。固然,爲了保證表的更新可以實現,我使用了多個表,一個表確定辦不成。由於你 即要知道當前要下載的網絡地址,還要知道你已經下載過的網絡地址。你要把已經下載過的地址重新的網頁中獲取到的網址列表中除去,這當中又涉及了一些臨時的 表。
     爬蟲在下載網頁的時候,最好還要把哪一個網頁存到了哪一個文件記錄好,而且記錄好網頁是搜索到廣度搜索到的第幾層的深度記錄好,由於若是要作搜索引擎,這個都 是對製做索引和對網頁排序有參考價值的信息。至少你本身會想知道爬蟲給你下載到了什麼,都放在哪了吧。對應的寫記錄的語句我在代碼裏的行末用##標註出來 了。
     寫的文字已經不少了,不想再寫了,直接貼上代碼:

文件Test.py內容以下:(它調用了WebCrawler,運行時是運行它)
-------------------------------------------------------
-
# -*- coding: cp936 -*-
import WebCrawler

url = raw_input('設置入口url(例-->http://www.baidu.com): \n')
thNumber = int(raw_input('設置線程數:'))    #以前類型未轉換出bug

wc = WebCrawler.WebCrawler(thNumber)
wc.Craw(url)


文件WebCrawler.py內容以下:
--------------------------------------------------------

# -*- coding: cp936 -*-
import threading
import GetUrl
import urllib

g_mutex = threading.Lock()
g_pages = []      #線程下載頁面後,將頁面內容添加到這個list中
g_dledUrl = []    #全部下載過的url
g_toDlUrl = []    #當前要下載的url
g_failedUrl = []  #下載失敗的url
g_totalcount = 0  #下載過的頁面數

class WebCrawler:
    def __init__(self,threadNumber):
        self.threadNumber = threadNumber
        self.threadPool = []
        self.logfile = file('#log.txt','w')                                   ##

    def download(self, url, fileName):
        Cth = CrawlerThread(url, fileName)
        self.threadPool.append(Cth)
        Cth.start()

    def downloadAll(self):
        global g_toDlUrl
        global g_totalcount
        i = 0
        while i < len(g_toDlUrl):
            j = 0
            while j < self.threadNumber and i + j < len(g_toDlUrl):
                g_totalcount += 1    #進入循環則下載頁面數加1
                self.download(g_toDlUrl[i+j],str(g_totalcount)+'.htm')
                print 'Thread started:',i+j,'--File number = ',g_totalcount
                j += 1
            i += j
            for th in self.threadPool:
                th.join(30)     #等待線程結束,30秒超時
            self.threadPool = []    #清空線程池
        g_toDlUrl = []    #清空列表

    def updateToDl(self):
        global g_toDlUrl
        global g_dledUrl
        newUrlList = []
        for s in g_pages:
            newUrlList += GetUrl.GetUrl(s)   #######GetUrl要具體實現
        g_toDlUrl = list(set(newUrlList) - set(g_dledUrl))    #提示unhashable
               
    def Craw(self,entryUrl):    #這是一個深度搜索,到g_toDlUrl爲空時結束
        g_toDlUrl.append(entryUrl)
        depth = 0
        while len(g_toDlUrl) != 0:
            depth += 1
            print 'Searching depth ',depth,'...\n\n'
            self.downloadAll()
            self.updateToDl()
            content = '\n>>>Depth ' + str(depth)+':\n'                         ##(該標記表示此語句用於寫文件記錄)
            self.logfile.write(content)                                        ##
            i = 0                                                              ##
            while i < len(g_toDlUrl):                                          ##
                content = str(g_totalcount + i) + '->' + g_toDlUrl[i] + '\n'   ##
                self.logfile.write(content)                                    ##
                i += 1                                                         ##
        
class CrawlerThread(threading.Thread):
    def __init__(self, url, fileName):
        threading.Thread.__init__(self)
        self.url = url    #本線程下載的url
        self.fileName = fileName

    def run(self):    #線程工做-->下載html頁面
        global g_mutex
        global g_failedUrl
        global g_dledUrl
        try:
            f = urllib.urlopen(self.url)
            s = f.read()
            fout = file(self.fileName, 'w')
            fout.write(s)
            fout.close()
        except:
            g_mutex.acquire()    #線程鎖-->鎖上
            g_dledUrl.append(self.url)
            g_failedUrl.append(self.url)
            g_mutex.release()    #線程鎖-->釋放
            print 'Failed downloading and saving',self.url
            return None    #記着返回!
       
        g_mutex.acquire()    #線程鎖-->鎖上
        g_pages.append(s)
        g_dledUrl.append(self.url)
        g_mutex.release()    #線程鎖-->釋放

文件GetUrl.py內容以下:(它裏面的GetUrl從一個存有網頁內容的字符串中獲取全部url並以一個list返回,這部分實現方法不少,你們能夠本身寫個更好的) -------------------------------------------------------- urlSep = ['<','>','\\','(',')', r'"', ' ', '\t', '\n'] urlTag = ['http://'] def is_sep(ch):     for c in urlSep:         if c == ch:             return True     return False def find_first_sep(i,s):     while i < len(s):         if is_sep(s[i]):             return i         i+=1     return len(s) def GetUrl(strPage):     rtList = []     for tag in urlTag:         i = 0         i = strPage.find(tag, i, len(strPage))         while i != -1:             begin = i             end = find_first_sep(begin+len(tag),strPage)             rtList.append(strPage[begin:end])             i = strPage.find(tag, end, len(strPage))     return rtList
相關文章
相關標籤/搜索