引言
週末沒事幹,無聊,使用php做了個博客抓取系統,我經常訪問的是cnblogs,當然從博客園(看看我還是很喜歡博客園的)開始入手了,我的抓取比較簡易,取得網頁內容,然後透過正規匹配,取得想要的東西,然後儲存資料庫,當然了,在實際過程中會遇到一些問題。做這個之前已經想好了,要做成可擴充的,以後要是哪天想添加csdn、51cto、新浪博客這些內容了可以很容易的擴展。
那些東西可以抓取?
首先要說些,這個是個簡易的抓取,不是所有網頁中看到的東西都可以抓取,有些東西是抓取不到的,就像下面這些
其中圈紅的閱讀次數、評論次數、推薦次數、反對次數、評論……,這些是透過js調用ajax動態獲取的,所以是獲取不到的,其實就一句話,你打開一個網頁,然後右鍵點擊查看原始碼,在原始碼中直接看不到的,這種簡易抓取可能就有問題,要抓取那些ajax填充的內容,要想想其他辦法,之前看見過一篇文章,有人先通過瀏覽器載入完網頁,然後對整個dom就行篩選(那篇文章也說了,這樣效率很低),當然了,拼接這些js請求也是可以的,估計會比較麻煩。
爬取的思路
先說下爬取深度depth
例如從鏈接a開始爬,如果depth是1,獲取玩當前鏈接的內容就完事,如果depth是2的話,就從a鏈接的內容中再去按指定的規則匹配鏈接,對匹配到的鏈接也做depth為1的處理,以此類推,depth是獲取連結的深度、層級。這樣爬蟲才可以」爬動起來「。
當然了,用一個連結去爬特定的內容,這個爬到的東西是很有限的,或者有可能還沒爬起來就死掉了(往後的層級沒有匹配到內容),所以在爬取的時候可以設定多個起始連結。當然了,在爬取的時候很可能會遇到很多重複的鏈接,所以還得給抓取到的鏈接做記號,防止重複獲取相同的內容,造成冗餘。有幾個變數來快取這些信息,格式如下
第一,就是一個hash數組,鍵值是url的md5值,狀態是0,維護一個不重複的url數組,形狀如下面的形式
<span>Array</span><span> ( [bc790cda87745fa78a2ebeffd8b48145] </span>=> 0<span> [9868e03f81179419d5b74b5ee709cdc2] </span>=> 0<span> [4a9506d20915a511a561be80986544be] </span>=> 0<span> [818bcdd76aaa0d41ca88491812559585] </span>=> 0<span> [9433c3f38fca129e46372282f1569757] </span>=> 0<span> [f005698a0706284d4308f7b9cf2a9d35] </span>=> 0<span> [e463afcf13948f0a36bf68b30d2e9091] </span>=> 0<span> [23ce4775bd2ce9c75379890e84fadd8e] </span>=> 0 ......<span> )</span>登入後複製
第二個就是要獲取的url數組,這個地方還可以優化,我是將所有的鏈接鏈接全部獲取到數組中,再去循環數組獲取內容,就等於是說,所有最大深度減1的內容都獲取了兩次,這裡可以直接在獲取下一級內容的時候順便把內容獲取了,然後上面的數組中狀態修改為1(已經獲取),這樣可以提高效率。先看看保存連結的陣列內容:
<span>Array</span><span> ( [</span>0] => <span>Array</span><span> ( [</span>0] => http:<span>//</span><span>zzk.cnblogs.com/s?t=b&w=php&p=1</span> <span> ) [</span>1] => <span>Array</span><span> ( [</span>0] => http:<span>//</span><span>www.cnblogs.com/baochuan/archive/2012/03/12/2391135.html</span> [1] => http:<span>//</span><span>www.cnblogs.com/ohmygirl/p/internal-variable-1.html</span> [2] => http:<span>//</span><span>www.cnblogs.com/zuoxiaolong/p/java1.html</span> ......<span> ) [</span>2] => <span>Array</span><span> ( [</span>0] => http:<span>//</span><span>www.cnblogs.com/ohmygirl/category/623392.html</span> [1] => http:<span>//</span><span>www.cnblogs.com/ohmygirl/category/619019.html</span> [2] => http:<span>//</span><span>www.cnblogs.com/ohmygirl/category/619020.html</span> ......<span> ) )</span>登入後複製最後將所有的連結拼為一個數組返回,讓程式循環獲取連接中的內容。就像上面的獲取層級是2,0級的鏈內容接獲取過了,僅用來獲取1級中的鏈接,1級中的所有鏈接內容也獲取過了,僅用來保存2級中的鏈接,等到真正獲取內容的時候又會對上面的內容進行一次獲取,而且上面的hash數組中的狀態都沒有用到。 。 。 (有待優化)。
還有一個獲取文章的正則,透過分析博客園中的文章內容,發現文章標題、正文部分基本上都可以很規則的獲取到
標題,標題html代碼的形式都是下圖的那種格式,可以很輕鬆的用下面的正則匹配到
<span>#</span><span><a\s*?id=\"cb_post_title_url\"[^>]*?>(.*?)<\/a>#is</span>登入後複製
正文,正文部分是可以透過正規表示式的高級特性平衡組很容易取得到的,但弄了半天發現php好像對平衡組支持的不是很好,所以放棄額平衡組,在html源碼中發現透過下面的正規也可以很容易地搭配到文章正文的內容,每篇文章基本上都有下圖中的內容
<span>#</span><span>(<div\s*?id=\"cnblogs_post_body\"[^>]*?>.*)<div\s*id=\"blog_post_info_block\">#is</span>登入後複製
開始:
結束:
博客的发布时间也是可以获取到的,但有些文章在获取发布时间的时候可能会找不到,这个就不列在这里了,有了这些东西就可以爬取内容了。
开始爬取
开始爬取内容了,最初我设置的爬取深度是2级,初始页面是博客园首页,发现爬取不了多少内容,后来发现博客园首页有个页码导航
就试图拼接成页码格式http://www.cnblogs.com/#p2,循环200次,以每页为起始页面,深度为2去抓取。但我高兴的太早了,开了几个进程跑了好久程序,抓了几十万条,后来发现完全在重复,都是从第一页中抓取的,因为博客园首页点击导航的时候(除了第一页),都是ajax请求获取到的。。。。看来博客园还是考虑到这个问题,因为大多数人都是只打开首页,不会去点击后面的内容(我可能偶尔会去点击下一页),所以为了在防止初级抓取者去抓取和性能发面做权衡,将第一页设置为静态网页的方式,缓存有效期是几分钟(或者是根据跟新频率,当更新多少篇的时候去更新缓存,或者两者的结合),这也是为什么有时候发布的文章,过一会儿才会显示出来的原因(我猜的^_^)。
难道不能一次性抓取很多内容吗?后来我发现这个地方使用的全部是静态网页
从找找看这个地方获取到的内容都是静态的,包括最下面的导航链接中的所有页面都是静态的,而且,这个搜索右边还有筛选条件,可以更好的提高抓取的质量。好了有了这个入口,就可以获取到好多高质量的文章了,下面是循环抓取100页的代码
<span>for</span>(<span>$i</span>=1;<span>$i</span><=100;<span>$i</span>++<span>){ </span><span>echo</span> "PAGE{<span>$i</span>}*************************[begin]***************************\r"<span>; </span><span>$spidercnblogs</span> = <span>new</span> C\Spidercnblogs("http://zzk.cnblogs.com/s?t=b&w=php&p={$i}"<span>); </span><span>$urls</span> = <span>$spidercnblogs</span>-><span>spiderUrls(); </span><span>die</span><span>(); </span><span>foreach</span> (<span>$urls</span> <span>as</span> <span>$key</span> => <span>$value</span><span>) { </span><span>$cnblogs</span>->grap(<span>$value</span><span>); </span><span>$cnblogs</span>-><span>save(); } }</span>登入後複製
至此,就可以去抓去自己喜欢的东西了,抓取速度不是很快,我在一台普通pc上面开了10个进程,抓了好几个小时,才获取到了40多万条数据,好了看看抓取到的内容稍微优化之后的显示效果,这里面加上了博客园的基础css代码,可以看出效果和
抓取到的内容稍作修改:
原始内容
再看下文件目录结构,也是用上篇的自制目录生成工具生成的:
+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)所有,未经作者本人同意禁止任何形式的转载,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
以上就介绍了博客爬取系统,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。