在服务器端或脚本环境下,我们经常需要从原始 HTML 中提取数据、清洗内容、重新构造页面片段等。与在浏览器内操作 DOM 不同,Node.js 本身不具备完整的浏览器环境。Cheerio 正是为这种场景设计的:它提供一种类似 jQuery 的 API,让你以极简方式操作 HTML 结构,而无需启动真正的浏览器。
Cheerio 解析速度快、体积小、使用门槛低,因此在 Web 抓取(web scraping)、内容处理与数据抽取等场景中被广泛使用。
下面我们先从原理与定位谈起,再进入安装、基本用法、进阶技巧与注意事项。
Cheerio 的定位与工作原理
1. 定位与优劣
-
Cheerio 提供对 HTML / XML 内容的解析与操作能力,但不执行 CSS 渲染、不运行 JavaScript、不加载资源,它只是一个标记语言内容处理工具。
-
与 Puppeteer、Playwright 这种完整浏览器模拟或 headless 浏览器相比,Cheerio 更轻量、启动快、资源占用低。
-
它适用于当目标页面结构稳定、内容在初始 HTML 中即可获得、无需 JS 动态渲染时。
-
如果页面依赖 heavy JavaScript 渲染或 API 动态加载,Cheerio 可能就不够,需要搭配浏览器自动化。
2. 解析引擎
Cheerio 在内部基于 htmlparser2 或 parse5(可选) 解析器来生成一个 DOM 树抽象。它不过度模拟浏览器行为,而是简化 DOM 模型,以便快速选择与操作元素。
通过 cheerio.load() 将 HTML 字符串转换为一个可操作上下文,接着你就可以用类似 jQuery 的选择器(CSS 选择器)来查找节点、读取文本、修改属性、增加子元素等。
安装与初始化
安装
在一个 Node.js 项目中,通过 npm 或 yarn 安装:
npm install cheerio
# 或者
yarn add cheerio
安装后,你可以在 ES 模块或 CommonJS 模式下引入:
// CommonJS
const cheerio = require('cheerio');
// ES 模块或 TypeScript
import * as cheerio from 'cheerio';
初始化 / 加载 HTML
最基础的启动方式是调用 cheerio.load(htmlString):
const html = `<div><h1>标题</h1><p>内容段落</p></div>`;
const $ = cheerio.load(html);
此时,$ 就类似于在浏览器中的 jQuery 对象,你可以以它为起点去查找、操作节点。
如果你希望禁止包裹默认的 <html><head><body> 元素结构,可以在 load 时传入第三个参数 false:
const $ = cheerio.load(htmlString, null, false);
此外,在新版 Cheerios 中,还支持 cheerio.fromURL(url) 直接从 URL 加载(异步)——但一般场景下我们更常用 HTTP 客户端(如 axios、got、node-fetch)先获取 HTML,再交给 Cheerio 处理。
核心 API 与常用操作
下面按功能来拆解 Cheerio 最常用的操作。
1. 选择元素(Selectors)
Cheerio 支持大部分 CSS 选择器语法:
-
$('div'):选择所有<div>元素 -
$('.className'):按类名选 -
$('#idName'):按 id 选 -
$('div > p')、$('ul li')、$('input[type="text"]')等组合语法 -
也支持链式选取、上下文参数、以及 jQuery 风格的选择器扩展
例如:
const items = $('ul.nav > li.active > a');
2. 读取内容 / 属性
常见方法有:
-
.text():获取选中元素的纯文本内容(去除 HTML 标签) -
.html():获取选中元素内部的 HTML 内容 -
.attr(name):获取属性值;也可以.attr(name, value)给属性赋值 -
.prop(name):操作属性(在某些场景下区别于 attr) -
.data(key):操作data-系列属性
示例:
const title = $('h1').text();
const linkHref = $('a.external').attr('href');
3. 操作 DOM 与插入/删除节点
Cheerio 支持对 DOM 结构进行修改:
-
.append(content):在选中元素内部尾部插入内容或节点 -
.prepend(content):在内部头部插入 -
.before(content)/.after(content):在当前节点前后插入 -
.remove():删除选中节点 -
.replaceWith(content):替换当前节点 -
.empty():清空子节点内容 -
.addClass(),.removeClass(),.toggleClass():操控 class -
.attr()、.css()(有限支持)等属性操作
例如:
$('ul').append('<li>新增项</li>');
$('p.intro').remove();
$('a').attr('target', '_blank');
$('div').addClass('highlight');
4. 遍历 / 循环
Cheerio 提供 .each() 方法,用来遍历一组元素:
$('li').each((index, element) => {
const $elem = $(element);
console.log(index, $elem.text());
});
注意:each 回调中 this 通常指向当前元素,也可以使用 $(this) 包裹成 Cheerio 对象。
有时还会用 .map() 方法,将一组节点映射成数组值(最后用 .get() 转为纯数组):
const texts = $('li').map((i, el) => {
return $(el).text().trim();
}).get();
5. 输出 / 渲染
当你完成对 DOM 的修改后,可以将结果重新转为 HTML 字符串:
const resultHtml = $.html(); // 整个文档的 HTML
const fragmentHtml = $('body').html(); // 某个节点内部的 HTML
这样你就可以把新的内容存储、输出,或者插入其他结构中。
一个抓取示例:结合 HTTP 请求使用
通常我们会把 Cheerio 和 HTTP 客户端(如 axios、got、node-fetch)结合,用来从远程网页抓取内容、解析出我们关心的数据。
下面是一个完整示例(使用 async/await + axios):
import axios from 'axios';
import * as cheerio from 'cheerio';
async function scrapePage(url) {
try {
const { data: html } = await axios.get(url);
const $ = cheerio.load(html);
// 假设我们要抓取文章列表
const articles = [];
$('div.article').each((i, el) => {
const $el = $(el);
const title = $el.find('h2.title').text().trim();
const link = $el.find('a.more').attr('href');
const summary = $el.find('p.summary').text().trim();
articles.push({ title, link, summary });
});
return articles;
} catch (error) {
console.error('抓取失败:', error.message);
return [];
}
}
(async () => {
const url = 'https://example.com/blog';
const list = await scrapePage(url);
console.log(list);
})();
在这个示例中:
- 用 axios.get 获取页面 HTML
- 用 cheerio.load 将 HTML 转为可查询对象
- 用 CSS 选择器定位每篇文章的 DOM 结构
- 读取子元素文本与链接属性
- 构建数据对象数组
你可以根据目标页面结构自定义选择器与字段。
进阶技巧与最佳实践
-
选择器要精准
尽量避免使用过于宽泛的选择器(如div、*),应结合 class、id、层级关系来锁定目标,减少错误匹配。 -
处理相对链接
抓取网页链接时常遇到相对路径。你可能需要用URL模块或字符串拼接,将相对地址转为完整 URL。 -
避免异常崩溃
在读取属性或文本之前,最好先判断元素存在(例如if ($el.length > 0))。 -
节制抓取速率 / 遵守 robots.txt
不要短时间内对同一网站频繁请求,需要设置延迟或并发限制,以防封 IP 或被目标站点屏蔽。 -
缓存与重用
若你可能多次操作同一页面 HTML,可以在内存中缓存解析结果,避免重复 load。 -
处理不合法 HTML / 容错
有时候网页 HTML 片段不完整、标签未闭合等,Cheerio 的解析器具有一定容错性。但是,在极端错误的标记下仍可能出错,这时可以先做预处理(如正则修补、清洗)再 load。 -
判断需不需要 JS 渲染
如果目标页面的数据由 JavaScript 动态生成(如通过 AJAX 拉取),Cheerio 可能拿不到数据。这时,你可能就要考虑 headless 浏览器或模拟 API 请求的方式。
常见误区与注意事项
-
Cheerio 不是浏览器:它不执行 JavaScript,不加载外部资源(如图片、CSS、脚本),不能模拟用户事件。
-
不要把它当作前端 DOM 操作文档:它适合用于服务器端 / 脚本中处理原始 HTML。
-
慎用
.html()直接插入:插入包含脚本标签、事件属性或不可信内容时要注意安全性(XSS 风险)。 -
依赖结构稳定性:如果目标网页结构频繁变化,抓取程序可能随时失效,需要维护。
-
字符编码处理:有些网站返回的 HTML 编码不是 UTF-8,可能需要先进行编码转换(如使用
iconv-lite等库)再交给 Cheerio 处理。
总结
Cheerio 是在 Node.js 环境下处理 HTML 与 XML 的利器,它凭借类似 jQuery 的 API、快速的解析性能与简单的使用方式,在网页抓取、内容抽取、静态 HTML 处理等领域得到广泛应用。掌握 Cheerio 的加载方法、元素选择、属性操作、DOM 修改与遍历技巧,你就能高效地从原始 HTML 中提取、重构、清洗你需要的数据。