爬蟲採集去重優化淺談

之前在作漏洞Fuzz爬蟲時,曾作過URL去重相關的工做,當時是參考了seay法師的文章以及網上零碎的一些資料,感受作的很簡單。近來又遇到相關問題,因而乎有了再次改進算法的念頭。php

首先,針對URL自己的去重,能夠直接對整塊URL進行處理。在參考網上的一些文章時,發現它們大多采用了 URL 壓縮存儲的方法。不過使用這些算法在數據量較大的時候,能大幅減少存儲的空間:css

爬蟲採集去重優化淺談

基於磁盤的順序存儲。html

基於Hash算法的存儲。python

基於MD5壓縮映射的存儲。web

基於嵌入式Berkeley DB的存儲。ajax

基於布隆過濾器(Bloom Filter)的存儲。redis

對於 URL 直接去重,主要涉及的是存儲優化方面,對於本文不是重點,這裏再也不細說。算法

而對於 URL 邏輯上的去重,則須要更多地追求數據的可用性,這是作測試工做須要去考量的。數據庫

這裏先給出 seay 文章中的類似度去重算法,大體是下面這樣的:json

def urlsimilar(url): hash_size=199999 tmp=urlparse.urlparse(url) scheme=tmp[0] netloc=tmp[1] path=tmp[2][1:] query=tmp[4] #First get tail if len(path.split('/'))>1: tail=path.split('/')[-1].split('.')[-1] #print tail elif len(path.split('/'))==1: tail=path else: tail='1' #Second get path_length path_length=len(path.split('/'))-1 #Third get directy list except last path_list=path.split('/')[:-1]+[tail] #Fourth hash path_value=0 for i in range(path_length+1): if path_length-i==0: path_value+=hash(path_list[path_length-i])%98765 else: path_value+=len(path_list[path_length-i])*(10**(i+1)) #get host hash value netloc_value=hash(hashlib.new("md5",netloc).hexdigest())%hash_size url_value=hash(hashlib.new("md5",str(path_value+netloc_value)).hexdigest())%hash_size return url_value

這段函數的大概做用是,最後它會根據算法返回一個hash值,這個hash值也就是該URL的hash類似度。若是兩個URL計算出的hash值最後比較相等,咱們則能夠判斷兩個URL是具備較高的類似度的。

可是這個函數應該是seay舉例時隨手提出的(這裏強調下,省得被噴,後文再也不細說),只是簡單作了demo,並無進行細化檢驗。在比較粗糙的狀況下,該算法確實能剔除一些簡單的參數重複的狀況,但一旦參數複雜或者url不規範,是不太能很好的進行去重的。

那麼在針對URL獲取的過程當中,咱們還能夠作的小優化有哪些呢?

日期時間命名

首先,咱們能夠根據日期來去重。咱們知道,在爬取一些Blog和和門戶等系統時,常常會遇到以日期命名的目錄。

這些目錄大概概括起來,存在相似下面的形式:

2010-11-11 10-11-11 20101111

固然,還有些文件會以時間+隨機值命名,也多是用unix時間戳命名,這些多是根據上傳和編輯時間來定義的。

筆者建議是,使用redis或者memcache之類等緩存型數據庫,將其直接存儲;或者在數據量較大的時候,考慮將其做臨時存儲,須要的時候再進行對比。

好比,一旦出現日期時間命名的目錄或靜態文件,咱們能夠考慮將其存儲爲下面的格式:

目錄層級

命名格式 

URL地址(或壓縮過的hash值)

有人可能說,在前面seay提出的那個案例裏,好像是能夠解決相似日期類似度的問題。那咱們先看看下面的例子,此處輸出仍然基於上面那個函數:

print urlsimilar('http://www.baidu.com/blog/2010-10-11/') print urlsimilar('http://www.baidu.com/blog/2010-10-13/') print urlsimilar('http://www.baidu.com/blog/2010-9-13/') print urlsimilar('http://www.baidu.com/whisper/2010-10-11/')

輸出結果以下:

110086 110086 37294 4842

咱們能夠看到,在普通狀況下,確實於相同父級目錄下,類似度算法是能夠判斷正確的。 可是一旦日期格式不規範,或者父級目錄存在必定的差別,這裏是不能很好的判斷的。

固然,咱們也能夠經過機器學習來完成去重的工做。不過就簡化工做而言,仍是可使用一些小Tips,根據規則匹配來作到。

靜態文件的去重

咱們知道,在爬取URL的過程當中,也會遇到許多靜態文件,如shtml、html、css等等。這些文件在大多數的狀況下,是沒有太大意義的。除非測試者傾向於使用「寧肯錯殺一百,毫不放過一個」的全量採集手法。

這時候,咱們能夠配置黑名單,創建文件後綴規則庫進行過濾。

固然,在這些靜態後綴的URL連接,也可能帶上參數混淆的狀況。 我的建議是,用於回調的json、xml等URL,裏面可能儲存敏感內容,儘可能別動;其餘類型的靜態文件,仍然採起將參數分離的方式,最後對URL進行去重存儲。

特定狀況的過濾

在爬取特定網站時,咱們能夠預先作好配置,指定過濾一些目錄和頁面,以節省大量時間資源。

反過來,咱們也能夠指定只爬取指定目錄下的頁面,定向獲取咱們想要的內容。

敏感頁面的感知

爬蟲採集去重優化淺談

在前面seay提出的demo算法中,在這種狀況下是有必定侷限的。好比咱們須要在敏感目錄下,儘量多的拿到文件信息。好比咱們爬取到了後臺管理目錄,可能會遇到下面的狀況:

print urlsimilar('http://www.baidu.com/blog/admin/login.php') print urlsimilar('http://www.baidu.com/blog/admin/manage_index.php') print urlsimilar('http://www.baidu.com/blog/admin/test.css')

輸出結果以下:

40768 40768 40768

很明顯有問題不是麼?

固然,咱們能夠經過對敏感頁面關鍵詞進行監控;或者也能夠指定後綴文件,進行白名單監控。

可是一旦這樣作,並且還想採用前面的hash算法的話,你們自行定義的過濾函數的優先級,確定須要大於該算法。而且,咱們在這樣作的過程當中,也應該考慮過濾成本的問題,建議採用選擇性啓用。

高頻敏感目錄的優待

可能在爬取的過程當中,部分爬蟲是兼用了目錄爆破的手段的。若是採用了這種手法而且匹配成功後,咱們能夠將該目錄下的內容單獨使用一份過濾規則,從而避免去重算法的誤判。

響應頁面的過濾

爬蟲採集去重優化淺談

對於某些網站來說,可能有很多頁面由於連接是失效的,會被冠以404頁面和50x錯誤。另外,在無權訪問的時候,可能網站會作30x跳轉和403目錄限制。

這些頁面沒有實質性內容,在大多數時候是沒有意義的,咱們能夠在配置文件裏對須要爬取的這類頁面作白名單,好比保留403頁面,或者存取30x跳轉前(後)的頁面。

WAF(警告)頁面過濾

爬蟲採集去重優化淺談

某些網站可能被裝上了WAF,在訪問頻率過快時,可能會獲得一個WAF的警告頁面。而在CMS自己就作了限制的狀況下,會以20x的響應碼展現一些沒有不存在的頁面。

固然,咱們能夠經過分佈式換代理的方式,去解決部分這樣的問題,這裏先很少作討論。

這時候,咱們能夠配置相應的次數閾值,若是某些頁面出現的次數過多,能夠將其標記爲警告(WAF)頁面,進而作出過濾處理。這裏對某頁面的識別,能夠經過黑名單關鍵字標記;或者嘗試計算頁面hash值,好比下面這樣:

content = urllib2.urlopen('http://www.test.com/').read() md5_sum = hashlib.md5() md5_sum.update(content) print md5_sum.hexdigest()

固然,咱們在實際計算頁面hash值和作關鍵字監控時,也可能因爲反爬蟲機制的存在(如添加隨機值),須要適時調整類似度來計算hash值或者採用其餘手段。固然這也會消耗更多的時間和機器資源。但某些特定的狀況下,可能也會帶來意想不到的收穫。

無心義參數頁面去重

咱們在採集頁面的過程當中,一樣有可能會遇到一些毫無心義的、高頻出現的多參數頁面。這類頁面多是回調頁面,也多是臨時渲染的隨機頁面。

在這裏,你們能夠經過前面處理WAF(警告)的方法進行過濾。固然,使用前面的hash算法也是能夠應對大部分狀況的。畢竟網站的這類的URL有限,沒必要爲了幾種特型去消耗更多的資源,這樣得不償失。

JS代碼中的URL

在咱們提取js代碼,也就是遇到ajax之類的交互狀況時,可能會遇到須要拼接的GET請求,或者直接能夠取用的POST請求。

這類的URL地址,最好是結合phantomjs等webkit,更方便地進行動態拼接。

它們會顯得比較特殊,可能僅僅返回狀態碼,也可能會返回實質性的敏感內容。這種狀況,就須要根據爬取者的要求,對爬取的過濾規則進行適應性調整。

總結

筆者這裏旨在提出一些對類似URL去重的小優化,可能效果有限,也可能存在未盡人意之處。

歡迎你們提出建議,但願少一些愛噴的童鞋,多一點討論的大牛,與諸君共勉。

參考文章

如何避免重複抓取同一個網頁 https://segmentfault.com/q/1010000002664904

淺談動態爬蟲與去重 http://bobao.360.cn/learning/detail/3391.html

網絡爬蟲:URL去重策略之布隆過濾器(BloomFilter)的使用 http://blog.csdn.net/lemon_tree12138/article/details/47973715

實用科普:爬蟲技術淺析 編寫爬蟲應注意的點 http://www.cnseay.com/?p=4102

網絡爬蟲 (spider) URL消重設計 URL去重設計 http://woshizn.iteye.com/blog/532605

相關文章
相關標籤/搜索