網(wǎng)絡(luò)抓取要識別Web頁面穴亏,并將其轉(zhuǎn)換成結(jié)構(gòu)化數(shù)據(jù)蜂挪。比如說,你要負(fù)責(zé)升級出版社那古老的靜態(tài)網(wǎng)站嗓化,需要把之前的頁面下載下來棠涮,經(jīng)過分析后提取所有圖書的書名、介紹刺覆、作者和售價严肪。你肯定不想自己手工完成這項任務(wù),所以決定寫個Node程序來做這件事谦屑。這種程序就是網(wǎng)絡(luò)抓取器驳糯。
—— 《Node.js實戰(zhàn)》 (第2版) P267
Node.js實戰(zhàn) 封面
找個出版社的靜態(tài)網(wǎng)頁,圖靈社區(qū)不就是個正好的對象嗎??氢橙,那就以Node.js實戰(zhàn)(第2版)這本書為例吧酝枢!
1、提取圖書書名
詳情頁中圖書名稱HTML代碼
<h2>
Node.js實戰(zhàn)(第2版)
</h2>
提取片段的cheerio代碼如下
const html = `
<h2>
Node.js實戰(zhàn)(第2版)
</h2>
`;
const cheerio = require('cheerio');
const $ = cheerio.load(html);
console.log($('.book-title h2').text().trim());
2悍手、提取簡介
<div class="book-intro readmore">
本書是Node.js的實戰(zhàn)教程帘睦,涵蓋了為開發(fā)產(chǎn)品級Node應(yīng)用程序所需要的一切特性、技巧以及相關(guān)理念谓苟。 從搭建Node開發(fā)環(huán)境官脓,到一些簡單的演示程序,到開發(fā)復(fù)雜應(yīng)用程序所必不可少的異步編程涝焙。第2版介紹了全棧開發(fā)者所需的全部技術(shù),包括前端構(gòu)建系統(tǒng)孕暇、選擇Web框架仑撞、在Node中與數(shù)據(jù)庫的交互、編寫測試和部署Web程序妖滔,等等隧哮。
</div>
提取代碼如下
$('.book-intro').text().trim();
3、提取作者
上面兩個比較簡單座舍,只是熱熱身沮翔,作者的提取就稍微有些麻煩了。
HTML代碼
<div class="book-author">
<span>
[英] 亞歷克斯?楊 等 (作者)
</span>
<span>
<a href="/space/87796">吳海星</a> (譯者)
</span>
</div>
可以看到有一個作者曲秉,有一個譯者采蚀,怎么把他們分別提取出來呢。我們先看看下面的代碼
console.log($('.book-author').children().length);
運行的結(jié)果是2承二,說明在book-author這個節(jié)點下面有兩個子節(jié)點榆鼠,這與我們看到的HTML代碼相符。
用一個each函數(shù)遍歷亥鸠,并把它們分別打印出來:
$('.book-author').children().each((i, e)=>{
console.log($(e).text().trim());
});
再做些進(jìn)一步的處理
$('.book-author').children().each((i, e)=>{
let 名字 = $(e).text().trim();
if (名字.indexOf('(作者)') != -1) {
console.log('作者:', 名字.replace(/\(作者\)/, '').trim());
}
if (名字.indexOf('(譯者)') != -1) {
console.log('譯者:', 名字.replace(/\(譯者\)/, '').trim());
}
});
完整代碼
const superagent = require('superagent');
const cheerio = require('cheerio');
const url = 'http://www.ituring.com.cn/book/1993';
superagent.get(url).end( function(err, res) {
// 拋錯攔截
if (err) {
return
throw Error(err)
}
const book = {};
let $ = cheerio.load(res.text,{
decodeEntities: false
});
book.title = $('.book-title h2').text().trim();
book.intro = $('.book-intro').text().trim();
book.status = 出版狀態(tài) = $('li:contains("出版狀態(tài)")').text().replace(/出版狀態(tài)/, '');
$('.book-author').children().each((i, e)=>{
let 名字 = $(e).text().trim();
if (名字.indexOf('(作者)') != -1) {
book.auther = 名字.replace(/\(作者\)/, '').trim();
}
if (名字.indexOf('(譯者)') != -1) {
book.translator = 名字.replace(/\(譯者\)/, '').trim();
}
});
let 定價 = $('li:contains("定 價")').text().replace(/定 價/, '');
if (定價) {
book.price = 定價;
let 有電子書 = false;
let 找電子書 = $('dt').filter( function() {
let 有嗎 = $(this).text().trim() === '電子書';
if (有嗎 === true) {
有電子書 = true;
return true;
}
});
if (有電子書) {
book.ePrice = 找電子書.next().children('.price').text().trim();
}
}
book.tags = [];
$('.post-tag').each((i, e)=>{
book.tags.push($(e).text());
});
console.log(book);
});