借助 Node.js 生态系统中的第三方模块 cheerio ,可以很轻松地从 html 中抽取数据。cheerio 是一个 HTML 解析器, 其实现灵活、快速、精简,API 非常接近 jQuery 。下面的代码,以爬取新浪博客为使用场景,展示如何抽取博客标题、每篇博文的标题、正文、时间、分类、图片等数据。
const urlParse = require('url').parse
const path = require('path')
const cheerio = require('cheerio')
const isUrl = require('is-url')
const htmlDownloader = require('./htmlDownloader')
module.exports = async function extract(uid) {
let data = {uid, urls: [], post: [], imgs: []}
// 遍历博文目录,抽取博客标题,博文地址
let i = 1, page = 1
do {
let list = await htmlDownloader(`http://blog.sina.com.cn/s/articlelist_${uid}_0_${i}.html`).catch(console.error)
let $ = cheerio.load(list)
$('.articleList a[title]').each((i, el) => {
let href = $(el).attr('href')
if (isUrl(href)) data.urls.push(href)
})
if (i === 1) {
page = ($('span[style]').text().match(/\d+/) || [0])[0] // 总页数
data.title = $('title').text().split('_')[1] // 博客标题
data.link = $('.blogtitle a').attr('href') // 博客链接
}
} while (i++ <= page)
// 遍历博文地址,抽取博文内容(标题、正文、时间、分类、图片、原文链接)
for (let link of data.urls) {
let post = await htmlDownloader(link).catch(console.error)
let $ = cheerio.load(post, {decodeEntities: false})
let title = $('.titName').text() // 博文标题
let date = $('.time.SG_txtc').text().replace(/(\(|\))/g, '') // 博文发布时间
let cate = $('.blog_class a').text() // 博文分类
let content = ''
// 有2种博文页面,根据title判断
if (title) {
content = $('div.articalContent').html()
} else {
title = $('.h1_tit').text()
content = $('div.BNE_cont').html()
}
// 去掉span、font、a等无用标签,抽取正文内容中的图片地址,并替换为本地图片地址
content.replace(/<(p)[^>]*/gi, '<$1')
.replace(/\n?<\/?(span|font)[^>]*>\n?/gi, '')
.replace(/<a[^>]*><\/a>/gi, '')
.replace(/<img[^>]*real_src="([^"]*)"([^>]*)>/gi, (m, url, b) => {
url = url.replace(/&\d*/gi, '');
let name = path.parse(urlParse(url).pathname).base
if (!data.imgs.find(it => it.url === url)) {
data.imgs.push({name, url})
}
return `<img src="../imgs/${name}"${b}>`
})
.replace(/<a[^>]*>(<img[^>]*>)<\/a>/gi, '$1')
.trim()
data.post.push({title, date, cate, link, content})
}
// 返回抽取的数据
return data
}
上述代码使用了第三方模块 is-url ;使用正则表达式去除一些无用的标签,如span、font;还使用正则表达式抽取页面中的图片地址,并替换为本地地址,这可以方便后续生成 html 页面,或者 epub、mobi 等格式的电子书。