<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/tips/3915

            0x00 前言


            網絡爬蟲(Web crawler),是一種“自動化瀏覽網絡”的程序,或者說是一種網絡機器人。它們被廣泛用于互聯網搜索引擎或其他類似網站,以獲取或更新這些網站的內容和檢索方式。它們可以自動采集所有其能夠訪問到的頁面內容,以便程序做下一步的處理。

            在WEB2.0時代,動態網頁盛行起來。那么爬蟲就應該能在頁面內爬到這些有javascript生成的鏈接。當然動態解析頁面只是爬蟲的一個技術點。下面,我將按照如下順序分享下面的這些內容的一些個人經驗(編程語言為Python)。

            1,爬蟲架構。

            2,頁面下載與解析。

            3,URL去重方法。

            4,URL相似性算法。

            5,并發操作。

            6,數據存儲

            7,動態爬蟲源碼分享。

            8,參考文章

            0x01 爬蟲架構


            談到爬蟲架構,不得不提的是Scrapy的爬蟲架構。Scrapy,是Python開發的一個快速,高層次的爬蟲框架,用于抓取web站點并從頁面中提取結構化的數據。Scrapy用途廣泛,可以用于數據挖掘、監測和自動化測試。Scrapy吸引人的地方在于它是一個框架,任何人都可以根據需求方便的修改。它也提供了多種類型爬蟲的基類,如BaseSpider、sitemap爬蟲等。

            enter image description here

            上圖是Scrapy的架構圖,綠線是數據流向,首先從初始URL 開始,Scheduler 會將其交給 Downloader 進行下載,下載之后會交給 Spider 進行分析,需要保存的數據則會被送到Item Pipeline,那是對數據進行后期處理。另外,在數據流動的通道里還可以安裝各種中間件,進行必要的處理。 因此在開發爬蟲的時候,最好也先規劃好各種模塊。我的做法是單獨規劃下載模塊,爬行模塊,調度模塊,數據存儲模塊。

            0x02 頁面下載與解析


            頁面下載

            頁面下載分為靜態和動態兩種下載方式。

            傳統爬蟲利用的是靜態下載方式,靜態下載的優勢是下載過程快,但是頁面只是一個枯燥的html,因此頁面鏈接分析中獲取的只是< a >標簽的href屬性或者高手可以自己分析js,form之類的標簽捕獲一些鏈接。在python中可以利用urllib2模塊或requests模塊實現功能。 動態爬蟲在web2.0時代則有特殊的優勢,由于網頁會使用javascript處理,網頁內容通過Ajax異步獲取。所以,動態爬蟲需要分析經過javascript處理和ajax獲取內容后的頁面。目前簡單的解決方法是通過基于webkit的模塊直接處理。PYQT4、Splinter和Selenium這三個模塊都可以達到目的。對于爬蟲而言,瀏覽器界面是不需要的,因此使用一個headless browser是非常劃算的,HtmlUnit和phantomjs都是可以使用的headless browser。

            enter image description here

            以上這段代碼是訪問新浪網主站。通過對比靜態抓取頁面和動態抓取頁面的長度和對比靜態抓取頁面和動態抓取頁面內抓取的鏈接個數。

            enter image description here

            在靜態抓取中,頁面的長度是563838,頁面內抓取的鏈接數量只有166個。而在動態抓取中,頁面的長度增長到了695991,而鏈接數達到了1422,有了近10倍的提升。

            抓鏈接表達式

            正則:re.compile("href=\"([^\"]*)\"")

            Xpath:xpath('//*[@href]')

            頁面解析

            頁面解析是實現抓取頁面內鏈接和抓取特定數據的模塊,頁面解析主要是對字符串的處理,而html是一種特殊的字符串,在Python中re、beautifulsoup、HTMLParser、lxml等模塊都可以解決問題。對于鏈接,主要抓取a標簽下的href屬性,還有其他一些標簽的src屬性。

            0x03 URL去重


            URL去重是爬蟲運行中一項關鍵的步驟,由于運行中的爬蟲主要阻塞在網絡交互中,因此避免重復的網絡交互至關重要。爬蟲一般會將待抓取的URL放在一個隊列中,從抓取后的網頁中提取到新的URL,在他們被放入隊列之前,首先要確定這些新的URL沒有被抓取過,如果之前已經抓取過了,就不再放入隊列了。

            Hash表

            利用hash表做去重操作一般是最容易想到的方法,因為hash表查詢的時間復雜度是O(1),而且在hash表足夠大的情況下,hash沖突的概率就變得很小,因此URL是否重復的判斷準確性就非常高。利用hash表去重的這個做法是一個比較簡單的解決方法。但是普通hash表也有明顯的缺陷,在考慮內存的情況下,使用一張大的hash表是不妥的。Python中可以使用字典這一數據結構。

            URL壓縮

            如果hash表中,當每個節點儲存的是一個str形式的具體URL,是非常占用內存的,如果把這個URL進行壓縮成一個int型變量,內存占用程度上便有了3倍以上的縮小。因此可以利用Python的hashlib模塊來進行URL壓縮。 思路:把hash表的節點的數據結構設置為集合,集合內儲存壓縮后的URL。

            Bloom Filter

            Bloom Filter是通過極少的錯誤換取了存儲空間的極大節省。Bloom Filter 是通過一組k 個定義在n 個輸入key 上的Hash Function,將上述n 個key 映射到m 位上的數據容器。

            enter image description here

            上圖很清楚的說明了Bloom Filter的優勢,在可控的容器長度內,所有hash函數對同一個元素計算的hash值都為1時,就判斷這個元素存在。 Python中hashlib,自帶多種hash函數,有MD5,sha1,sha224,sha256,sha384,sha512。代碼中還可以進行加鹽處理,還是很方便的。 Bloom Filter也會產生沖突的情況,具體內容查看文章結尾的參考文章。

            在Python編程過程中,可以使用jaybaird提供的BloomFilter接口,或者自己造輪子。

            小細節

            有個小細節,在建立hash表的時候選擇容器很重要。hash表占用空間太大是個很不爽的問題,因此針對爬蟲去重,下列方法可以解決一些問題。

            enter image description here

            上面這段代碼簡單驗證了生成容器的運行時間。

            enter image description here

            由上圖可以看出,建立一個長度為1億的容器時,選擇list容器程序的運行時間花費了7.2s,而選擇字符串作為容器時,才花費了0.2s的運行時間。

            接下來看看內存的占用情況。

            enter image description here

            如果建立1億的列表占用了794660k內存。

            enter image description here

            而建立1億長度的字符串卻占用了109720k內存,空間占用大約減少了700000k。

            0x04 URL相似性


            初級算法

            對于URL相似性,我只是實踐一個非常簡單的方法。

            在保證不進行重復爬去的情況下,還需要對類似的URL進行判斷。我采用的是sponge和ly5066113提供的思路。具體資料在參考文章里。

            下列是一組可以判斷為相似的URL組

            http://auto.sohu.com/7/0903/70/column213117075.shtml

            http://auto.sohu.com/7/0903/95/column212969565.shtml

            http://auto.sohu.com/7/0903/96/column212969687.shtml

            http://auto.sohu.com/7/1103/61/column216206148.shtml

            http://auto.sohu.com/s2007/0155/s254359851/index1.shtml

            http://auto.sohu.com/s2007/5730/s249066842/index2.shtml

            http://auto.sohu.com/s2007/5730/s249067138/index3.shtml

            http://auto.sohu.com/s2007/5730/s249067983/index4.shtml

            按照預期,以上URL歸并后應該為

            http://auto.sohu.com/7/0903/70/column213117075.shtml

            http://auto.sohu.com/s2007/0155/s254359851/index1.shtml

            思路如下,需要提取如下特征

            1,host字符串

            2,目錄深度(以’/’分割)

            3,尾頁特征

            具體算法

            enter image description here

            算法本身很菜,各位一看就能懂。

            實際效果:

            enter image description here

            上圖顯示了把8個不一樣的url,算出了2個值。通過實踐,在一張千萬級的hash表中,沖突的情況是可以接受的。

            0x05 并發操作


            Python中的并發操作主要涉及的模型有:多線程模型、多進程模型、協程模型。Elias專門寫了一篇文章,來比較常用的幾種模型并發方案的性能。對于爬蟲本身來說,限制爬蟲速度主要來自目標服務器的響應速度,因此選擇一個控制起來順手的模塊才是對的。

            多線程模型

            多線程模型,是最容易上手的,Python中自帶的threading模塊能很好的實現并發需求,配合Queue模塊來實現共享數據。

            多進程模型

            多進程模型和多線程模型類似,multiprocessing模塊中也有類似的Queue模塊來實現數據共享。在linux中,用戶態的進程可以利用多核心的優勢,因此在多核背景下,能解決爬蟲的并發問題。

            協程模型

            協程模型,在Elias的文章中,基于greenlet實現的協程程序的性能僅次于Stackless Python,大致比Stackless Python慢一倍,比其他方案快接近一個數量級。因此基于gevent(封裝了greenlet)的并發程序會有很好的性能優勢。

            具體說明下gevent(非阻塞異步IO)。,“Gevent是一種基于協程的Python網絡庫,它用到Greenlet提供的,封裝了libevent事件循環的高層同步API。”

            從實際的編程效果來看,協程模型確實表現非常好,運行結果的可控性明顯強了不少, gevent庫的封裝易用性極強。

            0x06 數據存儲


            數據存儲本身設計的技術就非常多,作為小菜不敢亂說,但是工作還是有一些小經驗是可以分享的。

            前提:使用關系數據庫,測試中選擇的是mysql,其他類似sqlite,SqlServer思路上沒有區別。

            當我們進行數據存儲時,目的就是減少與數據庫的交互操作,這樣可以提高性能。通常情況下,每當一個URL節點被讀取,就進行一次數據存儲,對于這樣的邏輯進行無限循環。其實這樣的性能體驗是非常差的,存儲速度非常慢。

            進階做法,為了減少與數據庫的交互次數,每次與數據庫交互從之前傳送1個節點變成傳送10個節點,到傳送100個節點內容,這樣效率變有了10倍至100倍的提升,在實際應用中,效果是非常好的。:D

            0x07 動態爬蟲源碼分享


            爬蟲模型

            enter image description here

            目前這個爬蟲模型如上圖,調度模塊是核心模塊。調度模塊分別與下載模塊,析取模塊,存儲模塊共享三個隊列,下載模塊與析取模塊共享一個隊列。數據傳遞方向如圖示。

            爬蟲源碼

            實現了以下功能:

            動態下載

            gevent處理

            BloomFilter過濾

            URL相似度過濾

            關鍵字過濾

            爬取深度

            Github地址:https://github.com/manning23/MSpider

            代碼總體來說難度不大,各位輕噴。

            0x08 參考文章


            感謝以下分享的文章與討論

            http://security.tencent.com/index.php/blog/msg/34 http://www.pnigos.com/?p=217

            http://security.tencent.com/index.php/blog/msg/12 http://wenku.baidu.com/view/7fa3ad6e58fafab069dc02b8.html

            http://wenku.baidu.com/view/67fa6feaaeaad1f346933f28.html

            http://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/

            http://www.elias.cn/Python/PyConcurrency?from=Develop.PyConcurrency

            http://blog.csdn.net/HanTangSongMing/article/details/24454453

            http://blog.csdn.net/historyasamirror/article/details/6746217 http://www.spongeliu.com/399.html

            http://xlambda.com/gevent-tutorial/ http://simple-is-better.com/news/334

            http://blog.csdn.net/jiaomeng/article/details/1495500 http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=1337181

            http://www.tuicool.com/articles/nieEVv http://www.zhihu.com/question/21652316 http://code.rootk.com/entry/crawler

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线