帮你统计阅读量

又是一个简单的爬虫

效果

源码

我始终相信编程在有用的同时也是有趣的,github上就有很多有意思的小项目。爬虫肯定是其中有意思的一个点。
上次我想把阮大的《ECMAScript 6 入门》爬下来,放到一个文件里,结果代码写好之后,发现人家已经开源到github上了,于是白忙活一场。🤢好在我还是有点小强的基因的,所以就想写这么一个统计博客阅读量的东西。

其实之前写过一个浏览器上的,算不上什么爬虫,只有四行代码。毕竟是浏览器,有诸多限制,比如只能知道自己的,而且还要登陆。。。看哲理

统计访问量的几种方法:

逛别人的博客可以发现,博客园里不少人的主页里有统计访问量的这么一个图片之类的东西:

像我这样的

高级一点的:

包括cnzz站长统计,

道理都是一样的,当有人访问你的主页时,由于你的主页上引用了统计网站的资源,比如图片,在请求的过程中,统计网站完成计数+1。但是这样的话,当刷新或者翻页时,访问量也会增加。所以访问量不等于阅读量。

知识点

  • promise的应用
  • http模块发起请求
  • cheerio可选
  • 正则表达式的应用

步骤

  1. 了解博客园的url结构。
    这一步比较简单,翻翻自己的博客就能发现,在翻页的时候,发生改变的是查询字符串“?page=1,2,3...”;所以在发起请求的时候,通过改变查询字符串参数来统计博客每一页的阅读量。

  2. 先统计一页的总阅读量
    就是用http模块向对应url发起请求,得到页面的html数据后,利用正则匹配出我们想要的数据,代码如下:

    var http=require('http'),
        root='http://www.cnblogs.com/imgss/';
        http.get(root +'?page=1', (res) => {
        var status = res.statusCode;
        var html = '';
        if(status == "200") {
            res.on('data', (data) => {
                html += data;
            });
            res.on('end', () => {
                var $ = cheerio.load(html);
                countStr = $('.postDesc').text();
                console.log(countStr);
                console.log('-----------------------------------------------')
                var re = /阅读\((\d+)\)/g;
                if(!countStr) {
                    return false;
                }
                while(true) {
                    var match = re.exec(countStr); //匹配阅读量数据
                    if(match)
                        total += +match[1];//将匹配到的数据加到total里面
                    else
                        break;
                }
                console.log('page1的阅读量是', total);
            })
        }
    })
    
    

    这里,countStr = $('.postDesc').text();部分是先用cheerio对html进行筛选,得到.postDesc元素下面的text,然后用re表达式进行进一步匹配得到阅读量的数据

    这里直接用re表达式匹配html也是可行的。只要摘要部分没有类似于"阅读()"的字符就行。

3 用promise管理并发请求。

    首先,我们把上面写的代码封装成一个模块。以便后面调用:
var cheerio = require('cheerio'),
    http = require('http');

module.exports = function(root, page) {//函数需要两个参数,一个是root,表示根路径,另一个是page,是一个number,表示请求第几页。
    return new Promise(function(resolve, reject) {//返回一个promise实例。
        var total = 0;
        http.get(root + `/default.html?page=${page}`, (res) => {
            var status = res.statusCode;
            var html = '';
            if(status == "200") {
                res.on('data', (data) => {
                    html += data;
                });
                res.on('end', () => {
                    if(!/class=\"day\"/.test(html)) {
                        console.log(`页面${page}没有内容`);
                        resolve(0);
                        return;
                    }
                    var $ = cheerio.load(html);
                    countStr = $('.postDesc').text();
                    var re = /阅读\((\d+)\)/g;
                    if(!countStr) {
                        resolve(0);//页面没有数据时,返回0
                    }
                    while(true) {
                        var match = re.exec(html); //匹配阅读量数据
                        if(match)
                            total += +match[1];//对当前页面的阅读量求和
                        else
                            break;
                    }
                    console.log(`page${page}的阅读量是`, total);
                    resolve(total);
                })
            }
        })
    })
}

接下来,就是通过一个主循环来得到所有的页面的总阅读量,首先引入上面的模块:

var onepage = require('./onepage');//引入上面的模块;
var promiseArr = [],
    host = 'http://www.cnblogs.com/';
host += process.argv[2] ? process.argv[2] : 'imgss' + '/';//可以接受来自命令行的参数,比如可以是`node index artech`
for(let i = 1; i < 10; i++) {
    promiseArr.push(onepage(host, i));//将promise实例放到一个数组里。
}
Promise.all(promiseArr).then(function(data) {//data是存放reslove()返回值的数组。
    let sum = 0;
    for(d of data) {
        sum += d;
    }
    console.log(sum);
})

Promise.all方法就像是一个管门的,promiseArr就像一个队列,当里面的实例都变成resolve,或者更形象一点,都成功了,Promise.all就成功了,然后就执行then方法。

4-19更新

上面的方法的缺陷在于:

  1. 没有控制并发,把i写成20,就会同时发起20个请求

  2. 没有自动结束的功能,就是即使后面的页面是空的,也会继续请求。

改进方法是用异步函数:像下面这样:

var onepage = require('./onepage');
var promiseArr = [],
    host = 'http://www.cnblogs.com/';
host += process.argv[2] ? process.argv[2] : 'imgss' + '/';
let i=1,sum=0;
(async function getAll(){
while(true){
    let num= await onepage(host , i++);
    if(num){
    sum += num;  
    }else{
        break;
    }
}
console.log('总阅读量:',sum);
})();

效果:

ps:很高兴自己有一篇博客破千了😆

promise讲解;

posted @ 2017-03-27 21:39  饭特稠  阅读(2241)  评论(0编辑  收藏  举报