帮你统计阅读量
又是一个简单的爬虫
我始终相信编程在有用的同时也是有趣的,github上就有很多有意思的小项目。爬虫肯定是其中有意思的一个点。
上次我想把阮大的《ECMAScript 6 入门》爬下来,放到一个文件里,结果代码写好之后,发现人家已经开源到github上了,于是白忙活一场。🤢好在我还是有点小强的基因的,所以就想写这么一个统计博客阅读量的东西。
其实之前写过一个浏览器上的,算不上什么爬虫,只有四行代码。毕竟是浏览器,有诸多限制,比如只能知道自己的,而且还要登陆。。。看哲理
统计访问量的几种方法:
逛别人的博客可以发现,博客园里不少人的主页里有统计访问量的这么一个图片之类的东西:
像我这样的
高级一点的:
包括cnzz站长统计,
道理都是一样的,当有人访问你的主页时,由于你的主页上引用了统计网站的资源,比如图片,在请求的过程中,统计网站完成计数+1。但是这样的话,当刷新或者翻页时,访问量也会增加。所以访问量不等于阅读量。
知识点
promise
的应用http
模块发起请求cheerio
可选- 正则表达式的应用
步骤
-
了解博客园的url结构。
这一步比较简单,翻翻自己的博客就能发现,在翻页的时候,发生改变的是查询字符串“?page=1,2,3...”
;所以在发起请求的时候,通过改变查询字符串参数来统计博客每一页的阅读量。 -
先统计一页的总阅读量
就是用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更新
上面的方法的缺陷在于:
-
没有控制并发,把i写成20,就会同时发起20个请求
-
没有自动结束的功能,就是即使后面的页面是空的,也会继续请求。
改进方法是用异步函数:像下面这样:
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讲解;