nodejs爬取博客园的博文

Posted 卡卡小狮子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nodejs爬取博客园的博文相关的知识,希望对你有一定的参考价值。

 

其实写这篇文章,我是很忐忑的,因为爬取的内容就是博客园的,万一哪个顽皮的小伙伴拿去干坏事,我岂不成共犯了?

 

好了,进入主题。

 

首先,爬虫需要用到的模块有:

express

ejs

superagent (nodejs里一个非常方便的客户端请求代理模块)

cheerio (nodejs版的jQuery)

 

前台布局使用bootstrap

分页插件使用 twbsPagination.js 

 

完整的爬虫代码,在我的github中可以下载。主要的逻辑代码在 router.js 中。

 

1. 爬取某个栏目第1页的数据

分析过程:

打开博客园的主页: http://www.cnblogs.com/

左侧导航栏里显示了所有栏目的分类信息,可以在开发者工具中获取查看这些信息.

 

每个栏目的URL也很有规律,都是 www.cnblogs.com/cate/栏目名称。 根据这个URL就可以爬取某个栏目第1页的博文了~

 

下面贴出代码:

app.js (入口文件)

 1 // 载入模块
 2 var express = require(\'express\');
 3 var app = express();
 4 var router = require(\'./router/router\');
 5 
 6 // 设置模板引擎
 7 app.set(\'view engine\', \'ejs\');
 8 
 9 // 静态资源中间件
10 app.use(express.static(\'./public\'));
11 
12 // 博客园
13 app.get(\'/cnblogs\', router.cnblogs);
14 // 栏目
15 app.get(\'/cnblogs/cate/:cate/\', router.cnblogs_cate);
16 
17 
18 app.listen(1314, function(err){
19     if(err) console.log(\'1314端口被占用\');
20 });

 

router.js 

var request = require(\'superagent\');
var cheerio = require(\'cheerio\');

// 栏目
var cate = [
    \'java\', \'cpp\', \'php\', \'delphi\', \'python\', \'ruby\',
    \'web\', \'javascript\', \'jquery\', \'html5\'
];

// 显示页面
exports.cnblogs = function(req, res){
    res.render(\'cnblogs\', {
        cate: cate
    });
};

// 爬取栏目数据
exports.cnblogs_cate = function(req, res){

    // 栏目
    var cate = req.params[\'cate\'];

    request
    .get(\'http://www.cnblogs.com/cate/\' + cate)
    .end(function(err, sres){

        var $ = cheerio.load(sres.text);
        var article = [];
        $(\'.titlelnk\').each(function(index, ele){
            var ele = $(ele);
            var href = ele.attr(\'href\'); // 博客链接
            var title = ele.text();      // 博客内容
            article.push({
                href: href,
                title: title
            });            
        });
        res.json({
            title: cate,
            cnblogs: article
        });
    });
};

 

cnblogs.ejs 

只贴出核心代码

1 <div class="col-lg-6">
2           <select class="form-control" id="cate">
3             <option value="0">请选择分类</option>
4             <% for(var i=0; i<cate.length; i++){ %>
5               <option value="<%= cate[i]%>"><%= cate[i]%></option>
6             <% } %>
7           </select>
8 </div>

JS模板

    <script type="text/template" id="cnblogs">
      <ul class="list-group">
          <li class="list-group-item">
            <a href="{{= href}}" target="_blank">{{= title}}</a>
      </ul>
    </script>

Ajax请求

$(\'#cate\').on(\'change\', function(){
 var cate = $(this).val();
 if(cate == 0) return;
 $(\'.artic\').html(\'\');
 $.ajax({
   url: \'/cnblogs/cate/\' + cate,
   type: \'GET\',
   dataType: \'json\',
   success: function(data){
     var cnblogs = data.cnblogs;
     for(var i=0; i<cnblogs.length; i++){
       var compiled = _.template($(\'#cnblogs\').html());
       var art = compiled(cnblogs[i]);
       $(\'.artic\').append(art);
     }
   }
 });
});

 

输入: http://localhost:1314/cnblogs/  ,可以看到, 成功获取javascript下第1页数据。

 

2. 分页功能

以 http://www.cnblogs.com/cate/javascript/ 为例:

首先,分页的数据是Ajax调用后端接口返回的。

 

chrome的开发者工具中,可以看到,分页时,会向服务器发送两个请求,

 PostList.aspx 请求具体某页的数据.

 load.aspx 返回分页字符串.

 

我们重点分析 PostList.aspx 这个接口:

 

可以发现 请求方式是POST。

问题的重点是POST请求的数据是如何组装的?

分析源码,发现每个分页字符串都绑定了一个事件 -- aggSite.loadCategoryPostList()

 

查看页面源码,发现这个函数定义在 aggsite.js 文件里.

 

也就是下面这个函数.

 

重点是这行代码, 使用Ajax向后端发送请求.

this.loadPostList("/mvc/AggSite/" + aggSiteModel.ItemListActionName + ".aspx").

分析loadPostList 函数,可以发现POST的数据是变量aggSiteModel的值.

 

而 aggSiteModel 在页面中的定义:

至此前端的分析告一段落。 我们要做的,就是使用nodejs,模拟浏览器发送请求。

 

router.js

 1 exports.cate_page = function(req, res){
 2 
 3     var cate = req.query.cate;
 4     var page = req.query.page;
 5 
 6     var url = \'http://www.cnblogs.com/cate/\' + cate;
 7 
 8     request
 9     .get(url)
10     .end(function(err, sres){
11 
12         // 构造POST请求的参数
13         var $ = cheerio.load(sres.text);
14         var post_data_str = $(\'#pager_bottom\').prev().html().trim();
15         var post_data_obj = JSON.parse(post_data_str.slice(post_data_str.indexOf(\'=\')+2, -1));    
16 
17         // 分页接口
18         var page_url = \'http://www.cnblogs.com/mvc/AggSite/PostList.aspx\';
19         // 修改当前页
20         post_data_obj.PageIndex = page; 
21 
22         request
23         .post(page_url)
24         .set(\'origin\', \'http://www.cnblogs.com\') // 伪造来源
25         .set(\'referer\', \'http://www.cnblogs.com/cate/\'+cate+\'/\') // 伪造referer
26         .send(post_data_obj) // POST数据
27         .end(function(err, ssres){
28             var article = [];
29             var $$ = cheerio.load(ssres.text);
30             $$(\'.titlelnk\').each(function(index, ele){
31                 var ele = $$(ele);
32                 var href = ele.attr(\'href\');
33                 var title = ele.text();
34                 article.push({
35                     href: href,
36                     title: title
37                 });            
38             });
39             res.json({
40                 title: cate,
41                 cnblogs: article
42             });
43         });    
44     });
45 };

 

cate.ejs 分页代码

 1 $(\'.pagination\').twbsPagination({
 2   totalPages: 20, // 默认显示20页
 3   startPage: 1,
 4   visiblePages: 5,
 5   initiateStartPageClick: false,
 6   first: \'首页\',
 7   prev: \'上一页\',
 8   next: \'下一页\',
 9   last: \'尾页\',
10   onPageClick: function(evt, page){
11     $.ajax({
12       url: \'/cnblogs/cate_page?cate=\' + cate + \'&page=\' + page,
13       type: \'GET\',
14       dataType: \'json\',
15       success: function(data){
16         $(\'.artic\').html(\'\');
17         var cnblogs = data.cnblogs;
18          for(var i=0; i<cnblogs.length; i++){
19            var compiled = _.template($(\'#cnblogs\').html());
20            var art = compiled(cnblogs[i]);
21            $(\'.artic\').append(art);
22          }
23       }
24     });
25   }
26 });

 

输入:http://localhost:1314/cnblogs/cate,可以看到:

javascript栏目第1页数据

第2页数据

 

后话

至此,一个简单的爬虫就完成了。  其实爬虫本身并不难,难点在于分析页面结构,和一些业务逻辑的处理。

完整的代码,我已经放在github上,欢迎starn(☆▽☆)

由于是第一次写技术类的博客,文笔有限,才学疏浅,若有不正确的地方,欢迎广大博友指正。

 

参考资料:

《SuperAgent中文使用文档》

《通读cheerio API》

 

以上是关于nodejs爬取博客园的博文的主要内容,如果未能解决你的问题,请参考以下文章

使用nodejs下载风景壁纸

爬虫实战使用python爬取博客园的某一篇文章

python——关于简单爬取博客园班级成员发的博文的题目发布人阅读评论,再存到csv文件中

关于将博客搬家至博客园的声明

博客搬家说明!

无意间发现我的博客园的年龄有11年了