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》

 


免責聲明!

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



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