博客爬取系統


引言

  周末沒事干,無聊,使用php做了個博客抓取系統,我經常訪問的是cnblogs,當然從博客園(看看我還是很喜歡博客園的)開始入手了,我的抓取比較簡易,獲取網頁內容,然后通過正則匹配,獲取到想要的東西,然后保存數據庫,當然了,在實際過程中會遇到一些問題。做這個之前已經想好了,要做成可擴充的,以后要是哪天想添加csdn、51cto、新浪博客這些內容了可以很容易的擴展。

那些東西可以抓取?

  首先要說些,這個是個簡易的抓取,不是所有網頁中看到的東西都可以抓取,有些東西是抓取不到的,就像下面這些

2015-01-26_130418

  其中圈紅的閱讀次數、評論次數、推薦次數、反對次數、評論……,這些是通過js調用ajax動態獲取的,所以是獲取不到的,其實就一句話,你打開一個網頁,然后右鍵點擊查看源代碼,在源代碼中直接看不到的,這種簡易抓取可能就有問題,要抓取那些ajax填充的內容,要想想其他辦法,之前看見過一篇文章,有人先通過瀏覽器加載完網頁,然后對整個dom就行篩選(那篇文章也說了,這樣效率很低),當然了,拼接這些js請求也是可以的,估計會比較麻煩。

爬取的思路

  首先說下爬取深度depth

比如從鏈接a開始爬,如果depth是1,獲取玩當前鏈接的內容就完事,如果depth是2的話,就從a鏈接的內容中再去按指定的規則匹配鏈接,對匹配到的鏈接也做depth為1的處理,以此類推,depth是獲取鏈接的深度、層級。這樣爬蟲才可以”爬動起來“。

  當然了,用一個鏈接去爬特定的內容,這個爬到的東西是很有限的,或者有可能還沒爬起來就死掉了(往后的層級沒有匹配到內容),所以在爬取的時候可以設置多個起始鏈接。當然了,在爬取的時候很可能會遇到很多重復的鏈接,所以還得給抓取到的鏈接做記號,防止重復獲取相同的內容,造成冗余。有幾個變量來緩存這些信息,格式如下

第一,就是一個hash數組,鍵值是url的md5值,狀態是0,維護一個不重復的url數組,形如下面的形式

Array
(
    [bc790cda87745fa78a2ebeffd8b48145] => 0
    [9868e03f81179419d5b74b5ee709cdc2] => 0
    [4a9506d20915a511a561be80986544be] => 0
    [818bcdd76aaa0d41ca88491812559585] => 0
    [9433c3f38fca129e46372282f1569757] => 0
    [f005698a0706284d4308f7b9cf2a9d35] => 0
    [e463afcf13948f0a36bf68b30d2e9091] => 0
    [23ce4775bd2ce9c75379890e84fadd8e] => 0
    ......
)

 

 

第二個就是要獲取的url數組,這個地方還可以優化,我是將所有的鏈接鏈接全部獲取到數組中,再去循環數組獲取內容,就等於是說,所有最大深度減1的內容都獲取了兩次,這里可以直接在獲取下一級內容的時候順便把內容獲取了,然后上面的數組中狀態修改為1(已經獲取),這樣可以提高效率。先看看保存鏈接的數組內容:

Array
(
    [0] => Array
        (
            [0] => http://zzk.cnblogs.com/s?t=b&w=php&p=1
        )
    [1] => Array
        (
            [0] => http://www.cnblogs.com/baochuan/archive/2012/03/12/2391135.html
            [1] => http://www.cnblogs.com/ohmygirl/p/internal-variable-1.html
            [2] => http://www.cnblogs.com/zuoxiaolong/p/java1.html
                ......
        )

    [2] => Array
        (
            [0] => http://www.cnblogs.com/ohmygirl/category/623392.html
            [1] => http://www.cnblogs.com/ohmygirl/category/619019.html
            [2] => http://www.cnblogs.com/ohmygirl/category/619020.html
                ......
        )

)

 

最后將所有的鏈接拼為一個數組返回,讓程序循環獲取連接中的內容。就像上面的獲取層級是2,0級的鏈內容接獲取過了,僅僅用來獲取1級中的鏈接,1級中的所有鏈接內容也獲取過了,僅僅用來保存2級中的鏈接,等到真正獲取內容的時候又會對上面的內容進行一次獲取,而且上面的hash數組中的狀態都沒有用到。。。(有待優化)。

 

  還有一個獲取文章的正則,通過分析博客園中的文章內容,發現文章標題、正文部分基本都可以很規則的獲取到

標題,標題html代碼的形式都是下圖的那種格式,可以很輕松的用下面的正則匹配到

#<a\s*?id=\"cb_post_title_url\"[^>]*?>(.*?)<\/a>#is

2015-01-26_211653

 

正文,正文部分是可以通過正則表達式的高級特性平衡組很容易獲取到的,但弄了半天發現php好像對平衡組支持的不是很好,所以放棄額平衡組,在html源碼中發現通過下面的正則也可以很容易匹配到文章正文的內容,每篇文章基本都有下圖中的內容

#(<div\s*?id=\"cnblogs_post_body\"[^>]*?>.*)<div\s*id=\"blog_post_info_block\">#is

 

開始:

2015-01-26_212318

結束:

2015-01-26_212225 

  博客的發布時間也是可以獲取到的,但有些文章在獲取發布時間的時候可能會找不到,這個就不列在這里了,有了這些東西就可以爬取內容了。

開始爬取

  開始爬取內容了,最初我設置的爬取深度是2級,初始頁面是博客園首頁,發現爬取不了多少內容,后來發現博客園首頁有個頁碼導航

2015-01-26_213020  就試圖拼接成頁碼格式http://www.cnblogs.com/#p2,循環200次,以每頁為起始頁面,深度為2去抓取。但我高興的太早了,開了幾個進程跑了好久程序,抓了幾十萬條,后來發現完全在重復,都是從第一頁中抓取的,因為博客園首頁點擊導航的時候(除了第一頁),都是ajax請求獲取到的。。。。看來博客園還是考慮到這個問題,因為大多數人都是只打開首頁,不會去點擊后面的內容(我可能偶爾會去點擊下一頁),所以為了在防止初級抓取者去抓取和性能發面做權衡,將第一頁設置為靜態網頁的方式,緩存有效期是幾分鍾(或者是根據跟新頻率,當更新多少篇的時候去更新緩存,或者兩者的結合),這也是為什么有時候發布的文章,過一會兒才會顯示出來的原因(我猜的^_^)。

  難道不能一次性抓取很多內容嗎?后來我發現這個地方使用的全部是靜態網頁

2015-01-26_214209

     從找找看這個地方獲取到的內容都是靜態的,包括最下面的導航鏈接中的所有頁面都是靜態的,而且,這個搜索右邊還有篩選條件,可以更好的提高抓取的質量。好了有了這個入口,就可以獲取到好多高質量的文章了,下面是循環抓取100頁的代碼

for($i=1;$i<=100;$i++){
            echo "PAGE{$i}*************************[begin]***************************\r";
            $spidercnblogs = new C\Spidercnblogs("http://zzk.cnblogs.com/s?t=b&w=php&p={$i}");
            $urls = $spidercnblogs->spiderUrls();
            die();
            foreach ($urls as $key => $value) {
                $cnblogs->grap($value);
                $cnblogs->save();
            }
        }

  至此,就可以去抓去自己喜歡的東西了,抓取速度不是很快,我在一台普通pc上面開了10個進程,抓了好幾個小時,才獲取到了40多萬條數據,好了看看抓取到的內容稍微優化之后的顯示效果,這里面加上了博客園的基礎css代碼,可以看出效果和

抓取到的內容稍作修改:

2015-01-26_215557

 

原始內容

2015-01-26_215756 

 

 再看下文件目錄結構,也是用上篇的自制目錄生成工具生成的:

 +myBlogs-master
    +controller
        |_Blog.php
        |_Blogcnblogs.php
        |_Spider.php
        |_Spidercnblogs.php
    +core
        |_Autoload.php
    +interface
        |_Blog.php
    +lib
        |_Mysql.php
    +model
        |_Blog.php
    |_App.php

   今天又想到,抓取的時候用隊列應該比較合適,新抓取的鏈接放在隊頭,從隊尾獲取鏈接的內容並匹配生成新的鏈接插入隊頭,這樣思路應該更清晰,更簡單。

 

  效果還是很不錯的,這里再猜下推酷這種專門爬取的網站的工作方式,一個常駐進程,隔一段時間去獲取一次內容(比如說首頁),如果有新鮮的內容入庫,沒有的話放棄這次獲取的內容,等待下次獲取,當時間很小的時候就可以一篇不漏的抓取的”新鮮“的內容。

這是github地址:

github——myBlogs

  本文版權歸作者iforever(luluyrt@163.com)所有,未經作者本人同意禁止任何形式的轉載,轉載文章之后必須在文章頁面明顯位置給出作者和原文連接,否則保留追究法律責任的權利。

 

微信號:love_skills

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM