以前在做漏洞Fuzz爬蟲時,曾做過URL去重相關的工作,當時是參考了seay法師的文章以及網上零碎的一些資料,感覺做的很簡單。近來又遇到相關問題,於是乎有了再次改進算法的念頭。
首先,針對URL本身的去重,可以直接對整塊URL進行處理。在參考網上的一些文章時,發現它們大多采用了 URL 壓縮存儲的方法。不過使用這些算法在數據量較大的時候,能大幅減小存儲的空間:
基於磁盤的順序存儲。
基於Hash算法的存儲。
基於MD5壓縮映射的存儲。
基於嵌入式Berkeley DB的存儲。
基於布隆過濾器(Bloom Filter)的存儲。
對於 URL 直接去重,主要涉及的是存儲優化方面,對於本文不是重點,這里不再細說。
而對於 URL 邏輯上的去重,則需要更多地追求數據的可用性,這是做測試工作需要去考量的。
這里先給出 seay 文章中的相似度去重算法,大致是下面這樣的:
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