又是一個簡單的爬蟲

我始終相信編程在有用的同時也是有趣的,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講解;
