nodeJs初体验——爬虫

602 查看

一,缘起

由于菩提眼网站(http://www.putiyan.com)的搭建,需要将菩提眼新浪微博上(http://blog.sina.com.cn/baoqie)的内容迁移过来。

二,首先,搭建nodejs环境。

可以去nodejs官网来下载nodejs并安装http://nodejs.cn/

三,其次,搭建html静态服务器。

通过npm安装http模块 npm install http

  var http = require("http");
  http.createServer(function(req, res){
       // 设置字符编码(去掉中文会乱码)
      res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
      res.write('hello nodeJs');
      res.end();
  }).linsten(3000);

四,安装爬虫依赖(superagent超级代理,cheerio,eventproxy事件代理服务器,async异步流程控制,fs(File System))

安装 npm install superagent; npm install cheerio; npm install eventproxy;
npm install async; npm install fs;

    var http       = require("http"),
    fs            = require('fs'),
    superagent = require("superagent"),
    cheerio    = require("cheerio"),
    async      = require("async"),
    eventproxy = require('eventproxy');

var ep             = new eventproxy(),
    catchFirstUrl = 'http://www.cnblogs.com/',  //入口页面 
    pageNum       = 16, //要爬取文章的页数
    pageUrls       = [],  //存放收集文章页面网站    
    startDate     = new Date(), //开始时间
    endDate       = false;  //结束时间
    blogArrs       = {
                    "title": "",
                    "content": "",
                    "time": ""
                  },
      blogArrs = [];


//存储需要的爬的列表页面
for(var i=11 ; i<= 16 ; i++){
    pageUrls.push('http://blog.sina.com.cn/s/articlelist_1392517237_0_' + i + '.html');
}
// 主start程序
function start(){
    function onRequest(req, res){
        // 设置字符编码(去掉中文会乱码)
        res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
        // 当所有 'BlogArticleHtml' 事件完成后的回调触发下面事件
        ep.after('BlogArticleHtml', pageUrls.length*20, function(articleUrls){
          
              // 获取 BlogPageUrl 页面内所有文章链接
             for(var i = 0 ; i < articleUrls.length ; i++){
                res.write(articleUrls[i] +'<br/>');  
            } 

            console.log('articleUrls.length is'+ articleUrls.length +',content is :'+articleUrls);

            // 控制并发数
            var curCount = 0;
            var reptileMove = function(url,callback){
                // 延迟毫秒数
                var delay = parseInt((Math.random() * 30000000) % 1000, 10);
                curCount++;
                console.log('现在的并发数是', curCount, ',正在抓取的是', url, ',耗时' + delay + '毫秒');  
                  
                superagent.get(url)
                .end(function(err,sres){
                    // 常规的错误处理
                    if (err) {
                    console.log(err);
                    return;
                    }       

                    //sres.text 里面存储着请求返回的 html 内容
                    var $ = cheerio.load(sres.text);                

                    blogArr = {
                        "title": $('.titName').html(),
                        "content": $('.articalContent').html(),
                          "time": $('.articalTitle').find('.time').html().replace('(','').replace(')','')
                    }
                    
                    blogArrs.unshift(blogArr);
                });

                setTimeout(function() {
                    curCount--;
                    callback(null,url +'Call back content');
                }, delay);    
            };

            // 使用async控制异步抓取  
            // mapLimit(arr, limit, iterator, [callback])
            // 异步回调
            async.mapLimit(articleUrls, 5 ,function (url, callback) {
                  reptileMove(url, callback);
            }, function (err,result) {
                endDate = new Date();

                console.log('final:');
                console.log(result);                

                for(var i = 0; i < blogArrs.length; i++){
                    // 生成所需要的html代码
                      var blogHtml = '<item>'
                                  +'<title>' + blogArrs[i].title +'</title>'
                                  +'<pubDate>Thu, 28 Apr 2016 11:17:16 +0000</pubDate>'
                                  +'<dc:creator><![CDATA[admin]]></dc:creator>'
                                  +'<content:encoded><![CDATA['+ blogArrs[i].content +']]></content:encoded>'
                                  +'<wp:post_date><![CDATA['+ blogArrs[i].time +']]></wp:post_date>'
                                  +'<wp:comment_status><![CDATA[open]]></wp:comment_status>'
                                  +'<wp:ping_status><![CDATA[open]]></wp:ping_status>'
                                  +'<wp:post_name><![CDATA[70]]></wp:post_name>'
                                  +'<wp:status><![CDATA[publish]]></wp:status>'
                                  +'<wp:post_type><![CDATA[post]]></wp:post_type>'
                                  +'<wp:post_password><![CDATA[]]></wp:post_password>'
                                  +'<wp:is_sticky>0</wp:is_sticky>'
                                 +'</item>';

                    // 往result.html中追加生成的代码
                    fs.appendFile('result.html', blogHtml, function () {
                        console.log('追加内容完成');
                    });
                }

                res.write(''+ blogHtml)
                


            //统计结果
            res.write('<br/>');
            res.write('<br/>');
            res.write('/**<br/>');
            res.write(' * 爬虫统计结果<br/>');
            res.write('**/<br/>');
            res.write('1、爬虫开始时间:'+ startDate +'<br/>');
            res.write('2、爬虫结束时间:'+ endDate +'<br/>');
            res.write('3、耗时:'+ (endDate - startDate) +'ms' +' --> '+ (Math.round((endDate - startDate)/1000/60*100)/100) +'min <br/>');
            res.write('4、爬虫遍历的文章数目:'+ pageNum*20 +'<br/>');
          });
        });
        
        // 轮询 所有文章列表页
        pageUrls.forEach(function(pageUrl){
            superagent.get(pageUrl)
            .end(function(err, pres){
                console.log('fetch ' + pageUrl + ' successful');
                res.write('fetch ' + pageUrl + ' successful<br/>');

                  // 常规的错误处理
                if (err) { console.log(err); }

                // pres.text 里面存储着请求返回的 html 内容,将它传给 cheerio.load 之后
                // 就可以得到一个实现了 jquery 接口的变量,我们习惯性地将它命名为 `$`
                // 剩下就都是 jquery 的内容了
                var $ = cheerio.load(pres.text);
                var curPageUrls = $('.articleCell');
                for(var i = 0 ; i < curPageUrls.length ; i++){
                    var articleUrl = curPageUrls.eq(i).find('a').attr('href');
                    // 相当于一个计数器
                    ep.emit('BlogArticleHtml', articleUrl);
                }
            });
        })
    }

    http.createServer(onRequest).listen(3000);
}

exports.start= start;