博客爬取系统

引言

  周末没事干,无聊,使用php作了个博客抓取系统,我常常访问的是cnblogs,固然从博客园(看看我仍是很喜欢博客园的)开始入手了,个人抓取比较简易,获取网页内容,而后经过正则匹配,获取到想要的东西,而后保存数据库,固然了,在实际过程当中会遇到一些问题。作这个以前已经想好了,要作成可扩充的,之后要是哪天想添加csdn、51cto、新浪博客这些内容了能够很容易的扩展。php

那些东西能够抓取?

  首先要说些,这个是个简易的抓取,不是全部网页中看到的东西均可以抓取,有些东西是抓取不到的,就像下面这些css

2015-01-26_130418

  其中圈红的阅读次数、评论次数、推荐次数、反对次数、评论……,这些是经过js调用ajax动态获取的,因此是获取不到的,其实就一句话,你打开一个网页,而后右键点击查看源代码,在源代码中直接看不到的,这种简易抓取可能就有问题,要抓取那些ajax填充的内容,要想一想其余办法,以前看见过一篇文章,有人先经过浏览器加载完网页,而后对整个dom就行筛选(那篇文章也说了,这样效率很低),固然了,拼接这些js请求也是能够的,估计会比较麻烦。html

爬取的思路

  首先说下爬取深度depthjava

好比从连接a开始爬,若是depth是1,获取玩当前连接的内容就完事,若是depth是2的话,就从a连接的内容中再去按指定的规则匹配连接,对匹配到的连接也作depth为1的处理,以此类推,depth是获取连接的深度、层级。这样爬虫才能够”爬动起来“。git

  固然了,用一个连接去爬特定的内容,这个爬到的东西是颇有限的,或者有可能还没爬起来就死掉了(日后的层级没有匹配到内容),因此在爬取的时候能够设置多个起始连接。固然了,在爬取的时候极可能会遇到不少重复的连接,因此还得给抓取到的连接作记号,防止重复获取相同的内容,形成冗余。有几个变量来缓存这些信息,格式以下github

第一,就是一个hash数组,键值是url的md5值,状态是0,维护一个不重复的url数组,形以下面的形式ajax

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数组中的状态都没有用到。。。(有待优化)。sql

 

  还有一个获取文章的正则,经过分析博客园中的文章内容,发现文章标题、正文部分基本均可以很规则的获取到数据库

标题,标题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

    

相关文章
相关标签/搜索