邊做邊學效率更高,爬蟲是node的適用場景之一,關於爬蟲的另一篇文章
為了驗證“簡書上,經驗總結、資料歸集類技術文章更容易上熱榜”的猜想,可以做一個爬蟲:爬取簡書程式設計師專題熱門文章前999篇,統計每篇文章的程式碼塊數量(為什麼是統計程式碼塊數量,對於人來說,通過一篇文章的標題內容來判斷文章的型別是一件輕而易舉的事,然而對於計算機來說,這卻是一件困難的事情,這已經屬於人工智慧的範疇了。然而獲取文章的程式碼塊數量對於計算機來說就容易得多了,可以認為,程式碼塊為0或者較少的文章,屬於經驗類文章,程式碼塊數量較多的文章如果不是資料歸集的話,多半就屬於談原始碼實現的了)
目錄
知識點
- http.request:node http 模組的request方法可以作為http client向伺服器發起http請求,爬蟲需要向目標連結發起http請求來獲得頁面資訊
- cheerio:通過 http 請求到的頁面資訊,由於缺乏瀏覽器的dom解析,看起來就是一段凌亂的字串,實在糟糕。好在我們可以使用 cheerio 庫將其解析為 dom ,這樣我們就可以使用類似 jquery 的語法去分析頁面資訊
- promise:由於 node 單執行緒的特性,不可避免的需要用到大量非同步程式設計的寫法,層層巢狀的回撥寫法已經 low 了,來試試 promise 的寫法
實現步驟
拉取頁面列表
首先需要拿到程式設計師專題熱門列表的請求連結
ajax請求,需要使用chrome dev tools,拉到底部還能載入更多:
//order_by=likes_count 表示按照熱門進行排序
//page是分頁引數,每頁9條,我們可以通過改變page=0~100來拉取900篇文章
http://www.jianshu.com/collections/16/notes?order_by=likes_count&page=2複製程式碼
下一步通過這個連結拉取列表資料
/**
- 建立promise
*/
Seek.prototype.createPromise = function(i) {
var options = {
url: 'http://www.jianshu.com/collections/16/notes?order_by=likes_count&page=' + i,
type: 'get'
}
return new Promise(function(resolve, reject) {
options.callback = function(data, _setCookie) {
resolve(data);
}
request(options, null);
});
}複製程式碼
可以建立一個promise物件來發起request請求(request的封裝就不貼出來了,實驗證明,程式碼塊太多的文章不容易上熱榜~~)
起初我的做法是同時建立拉取1~100頁資料的100個promise物件,同時非同步的發起100個request請求,然而這樣的做法會有幾十個請求請求失敗(興許是簡書那邊做了限制),所以,還是耐心點,每次發起5個請求,直到100頁都請求成功
/**
* 遞迴的請求,每次併發的請求5個
*/
Seek.prototype.seek = function(callback) {
var self = this;
times++;
var ot = times;
var promise = Promise.all([
self.createPromise(times),
self.createPromise(++times),
self.createPromise(++times),
self.createPromise(++times),
self.createPromise(++times)
]);
promise.then(function(result) {
console.log("seekList totals:" + times);
pages = pages.concat(result);
if (times < totalPage) {
self.seek(callback);
} else {
callback(pages);
}
});
}複製程式碼
拿到所有的列表資料之後,就可以使用cheerio庫來分析列表頁面,抓取文章詳情連結(在這一步之前你同樣需要使用chrome dev tools工具分析頁面結構)
/**
* 使用cheerio載入列表頁面
*/
Analyse.prototype.load = function(data, i) {
return new Promise(function(resolve, reject) {
var $ = cheerio.load(data);
var pages = [];
var els = $('.article-list li');
if(els.length === 0) {
console.warn('load error page:' + i );
resolve([]);
}
els.each(function(index) {
if ($(this).attr('class') === 'have-img') {
pages.push($(this).children('a').attr('href'));
} else {
pages.push($(this).children('div').children('.title').children('a').attr('href'));
}
if(index === els.length - 1) {
resolve(pages);
}
});
});
}複製程式碼
拉取頁面詳情,分析統計
從上一步中拿到900篇熱門文章的地址之後,需要再次去抓取文章詳情頁面,同樣的每次查5篇,使用chrome dev tools分析得知,簡書文章的程式碼塊使用的都是
<code></code>
標籤,統計此標籤出現的數量就可以了
/**
* 建立promise
*/
Seek.prototype.createPromise = function(url) {
var options = {
url: 'http://www.jianshu.com' + url,
type: 'get'
}
return new Promise(function(resolve, reject) {
options.callback = function(data, _setCookie) {
var $ = cheerio.load(data);
//頁面標題
var title = $('h1.title').text();
//程式碼塊數量
var codes = $('code').length;
if(codes === 0) {//程式碼塊為0的總數
zeroCount++;
} else if(codes <= 10) {//程式碼塊為<=10的總數
oneToTen++;
} else if(codes <= 20) {//程式碼塊<=20的總數
elToTwo++;
} else {//程式碼塊>20的總數
beyondTwo++;
}
resolve({
title: title,
codes: codes
});
}
request(options, null);
});
}複製程式碼
生成統計頁面
資料總需要一個展示的地方,使用nunjucks作為頁面模板,注入抓取到的資料,在使用echarts生成統計圖表,就是這個feel,結果頁面
總結
開發爬蟲是一件很酷的事情,在這個過程中還能學到知識,提升學習興趣,從爬蟲做起~~
原始碼
下一篇文章講 Node 爬蟲進階,敬請關注
補充說明:程式碼大半年前些的,簡書的介面和頁面結構已經做了改版,可能抓取不到想要的結果,如果感興趣,可以按照思路和步驟改造現在的程式碼,自己動手豐衣足食