首先我們默認(rèn)你已經(jīng)安裝node開發(fā)環(huán)境,node開發(fā)IDE (什么webstrom vscode sublime網(wǎng)上很多工具不多做贅述)旷痕。以及npm(NodeJS安裝包管理工具)
這里我們測(cè)試爬蟲百家號(hào)的不同tab下的新聞列表
爬蟲的基本思路
- 通過chrom瀏覽器請(qǐng)求網(wǎng)站
- 檢查元素分析數(shù)據(jù)的結(jié)構(gòu)
- 通過network分析點(diǎn)擊tab網(wǎng)站對(duì)應(yīng)的請(qǐng)求url
- 獲取所有tab對(duì)應(yīng)的url進(jìn)行并發(fā)請(qǐng)求
- 解析源碼獲取對(duì)應(yīng)的新聞數(shù)據(jù)
- 包裝成我們想要的數(shù)據(jù)結(jié)構(gòu)
創(chuàng)建demo cd到一個(gè)空文件夾
1.初始化項(xiàng)目
npm init
2.創(chuàng)建爬蟲文件fetch.js
3.通過npm安裝superagent冷溶、cheerio命雀、async以及url爬蟲需要的技術(shù)框架
npm install superagent cheerio async url
開始編寫爬蟲代碼
1.首先要引用我們需要的技術(shù)框架
//請(qǐng)求網(wǎng)址
var request = require('superagent');
//解析網(wǎng)頁(yè)源碼
var cheerio = require('cheerio');
//控制并發(fā)數(shù)
var async = require('async');
//url處理
var URL = require('url');
//文件管理
var fs = require('fs');
var path = require('path');
2.第一次請(qǐng)求百家號(hào)獲取不同tab對(duì)應(yīng)的url
//要爬蟲的網(wǎng)站
var bjhVideoUrl = 'https://baijia.baidu.com/';
function startFetch() {
request
.get(bjhVideoUrl)
.end((err,res)=> {
if(err) {
console.log(err);
return;
}
//解析源碼獲取url
decodeHeaderUrl(res);
});
}
3.根據(jù)源碼解析網(wǎng)址獲取不同url 以及并發(fā)請(qǐng)求子tab對(duì)應(yīng)的網(wǎng)頁(yè)數(shù)據(jù)
function decodeHeaderUrl(res){
var headerUrls = [];
var $ = cheerio.load(res.text);
$('#header-wrapper .bjh-header-content .bjh-header-list li a').each((idx,element)=> {
var href = $(element).attr('href');
var title = $(element).text().trim();
var headerJson = {
'href':href,
'title':title
};
titleJsons.push(headerJson);
var url = URL.resolve(bjhVideoUrl,href);
console.log(url);
headerUrls.push(url);
});
//通過語(yǔ)法糖async并發(fā)請(qǐng)求 headerurls:url數(shù)組,2:并發(fā)數(shù)
async.mapLimit(headerUrls,2,function(url,cb){
requestByTitle(url,cb);
},function(err,result){//全部請(qǐng)求結(jié)束之后返回的包裝數(shù)據(jù)數(shù)組
console.log('fetch data success! fetch data count ' + result.length);
try {
//數(shù)據(jù)保存到db.json文件
fs.writeFileSync(path.join(__dirname,'db.json'),JSON.stringify(result,null,2),{'flag':'a'});
console.log('save fetch data to db.json success!');
} catch (error) {
console.log('save fetch data to db.json fail!');
}
});
}
4.解析子tab 來獲取我們要的新聞列表數(shù)據(jù)
var $ = cheerio.load(res.text);
var artEle = $('#articleList')
artEle.children().each((idx,element)=> {
var className = $(element).attr('class');
if (className === 'article-info'){
var imgEle = $(element).find('img');
var imgSrc = $(imgEle).attr('src');
var titleEle = $(element).find('.title').children()[0];
var title = $(titleEle).text().trim();
var authorEle = $(element).find('.author').children()[0];
var author = $(authorEle).text().trim();
var otherEle = $(element).find('.author').children()[1];
var other = $(otherEle).text().trim();
var titleJson = {
'type':className,
'images':[imgSrc],
'title':title,
'author':author,
'other':other
}
videoJsons.push(titleJson);
5.包裝我們想要的數(shù)據(jù)結(jié)構(gòu)
//根據(jù)不同tab 對(duì)應(yīng)不同的數(shù)據(jù)列表
function createResult(url,videos) {
var urlParse = URL.parse(url);
var index = 0;
if (urlParse.query) {
index = parseInt(urlParse.query.substr(4,1));
}
index = index > 0 ?index : 0;
var titleJson = titleJsons[index];
var videoJson = {
'title':titleJson['title'],
'videos':videos
}
return videoJson;
}
6.fetch.js爬蟲代碼編寫完畢罗心,接下來執(zhí)行fetch文件
node fetch
返回結(jié)果如下:
數(shù)據(jù)被保存到db.json文件如下:
到此爬蟲項(xiàng)目就結(jié)束了,源碼:github
本次爬蟲demo使用的相關(guān)框架:
*superagent:superagent 是一個(gè)輕量的,漸進(jìn)式的ajax api,可讀性好,學(xué)習(xí)曲線低,內(nèi)部依賴nodejs原生的請(qǐng)求api城瞎。采用鏈?zhǔn)骄幊踢M(jìn)行方法的調(diào)用渤闷,通俗易懂,便于理解脖镀。我們通過它請(qǐng)求網(wǎng)址獲取網(wǎng)頁(yè)源碼飒箭。
POST請(qǐng)求用例:
superagent
.post('/api/pet')
.send({ name: 'Manny', species: 'cat' })
.set('X-API-Key', 'foobar')
.set('Accept', 'application/json')
.end(function(res){
if (res.ok) {
alert('yay got ' + JSON.stringify(res.body));
} else {
alert('Oh no! error ' + res.text);
}
});
詳細(xì)介紹:CNode中文社區(qū)
*cheeio:cheerio是nodejs的抓取頁(yè)面模塊,為服務(wù)器特別定制的蜒灰,快速补憾、靈活、實(shí)施的jQuery核心實(shí)現(xiàn)卷员。適合各種Web爬蟲程序盈匾。會(huì)玩jQuery的朋友相信看一眼就會(huì)使用了。通過它來解析我們的網(wǎng)頁(yè)源碼獲取我們想要的數(shù)據(jù)內(nèi)容毕骡。
獲取數(shù)據(jù)用例:
var headerUrls = [];
var $ = cheerio.load(res.text);
$('#header-wrapper .bjh-header-content .bjh-header-list li a').each((idx,element)=> {
var href = $(element).attr('href');
var title = $(element).text().trim();
var headerJson = {
'href':href,
'title':title
};
titleJsons.push(headerJson);
var url = URL.resolve(bjhVideoUrl,href);
console.log(url);
headerUrls.push(url);
});
更多實(shí)用方法請(qǐng)上:CNode中文社區(qū)
*async:Async 模塊是比較有名的異步解決方案削饵,在爬蟲過程中如果并發(fā)請(qǐng)求較大岩瘦,有可能會(huì)被網(wǎng)站認(rèn)為惡意攻擊,所以我們通過async的eachLimit技術(shù)來控制并發(fā)數(shù)來減少風(fēng)險(xiǎn)窿撬。
并發(fā)數(shù)控制用例:
async.mapLimit(headerUrls,2,function(url,cb){
requestByTitle(url,cb);
},function(err,result){
console.log('fetch data success! fetch data count ' + result.length);
try {
fs.writeFileSync(path.join(__dirname,'db.json'),JSON.stringify(result,null,2),{'flag':'a'});
console.log('save fetch data to db.json success!');
} catch (error) {
console.log('save fetch data to db.json fail!');
}
});