cheerio vs htmlparser2 vs sax vs xml2js
服务端 HTML 与 XML 解析方案选型
cheeriohtmlparser2saxxml2js类似的npm包:

服务端 HTML 与 XML 解析方案选型

cheerio、htmlparser2、sax 和 xml2js 都是 Node.js 生态中用于处理标记语言(HTML/XML)的核心工具,但它们的抽象层级和适用场景截然不同。cheerio 提供了类似 jQuery 的 API,适合快速操作 DOM 结构;htmlparser2 是一个高性能的流式解析器,适合处理大型文档或自定义逻辑;sax 是底层的 XML 流解析器,专注于严格的标准合规;xml2js 则直接将 XML 转换为 JavaScript 对象,适合配置文件或数据交换场景。理解它们的差异能帮助开发者在爬虫、数据转换或服务端渲染中选择最合适的工具。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
cheerio16,686,84530,5301.01 MB819 个月前MIT
htmlparser204,794235 kB157 个月前MIT
sax01,15861.7 kB1073 个月前BlueOak-1.0.0
xml2js04,9603.44 MB2503 年前MIT

服务端 HTML 与 XML 解析:Cheerio、Htmlparser2、Sax 与 Xml2js 深度对比

在 Node.js 服务端开发中,处理 HTML 和 XML 数据是常见需求,无论是编写爬虫、解析配置文件还是处理第三方 API 响应。cheerio、htmlparser2、sax 和 xml2js 是四个最常用的工具,但它们的设计目标完全不同。有些提供类似 jQuery 的便捷操作,有些则提供底层的流式控制。本文将从架构层级、代码实现和实际场景三个维度进行深度对比。

🏗️ 抽象层级:从 DOM 操作到事件流

这四个包可以分为三个层级。cheerio 位于最高层,它构建了一个完整的 DOM 树,让你像操作浏览器一样操作服务端 HTML。xml2js 位于中间层,它将 XML 转换为 JS 对象树。htmlparser2 和 sax 位于底层,它们基于事件流(Stream),逐块读取数据,内存占用更低。

cheerio 加载整个文档到内存,提供链式调用。

// cheerio: 加载完整 DOM
const cheerio = require('cheerio');
const $ = cheerio.load('<div><h1 class="title">Hello</h1></div>');
const text = $('.title').text(); // 直接选择器查询

htmlparser2 默认是事件驱动,也可以构建 DOM,但更常用于流式处理。

// htmlparser2: 事件回调处理
const htmlparser2 = require('htmlparser2');
const parser = new htmlparser2.Parser({
  onopentag: (name, attribs) => {
    if (name === 'h1') console.log(attribs.class);
  }
});
parser.write('<div><h1 class="title">Hello</h1></div>');

sax 是严格的 XML 事件解析器,不构建 DOM。

// sax: 严格 XML 事件流
const sax = require('sax');
const parser = sax.parser(true); // true = strict
parser.onopentag = (node) => {
  if (node.name === 'h1') console.log(node.attributes.class);
};
parser.write('<div><h1 class="title">Hello</h1></div>');

xml2js 直接转换为对象,不暴露解析过程。

// xml2js: 转换为 JS 对象
const xml2js = require('xml2js');
const parser = new xml2js.Parser();
parser.parseString('<div><h1 class="title">Hello</h1></div>', (err, result) => {
  console.log(result.div.h1[0]._); // 访问文本内容
});

📥 数据提取方式:选择器 vs 回调 vs 对象

提取数据的方式直接影响代码的可读性和维护成本。

cheerio 使用 CSS 选择器,最直观。

// cheerio: CSS 选择器
const $ = cheerio.load('<ul><li id="a">1</li><li id="b">2</li></ul>');
const item = $('#a').text(); // 获取 id 为 a 的内容

htmlparser2 需要在回调中手动维护状态。

// htmlparser2: 手动状态维护
let target = '';
const parser = new htmlparser2.Parser({
  onopentag: (name, attribs) => { if (attribs.id === 'a') target = name; },
  ontext: (text) => { if (target) console.log(text); }
});
parser.write('<ul><li id="a">1</li><li id="b">2</li></ul>');

sax 同样需要手动处理事件,且严格区分标签和文本。

// sax: 事件状态机
let capture = false;
const parser = sax.parser(true);
parser.onopentag = (node) => { if (node.attributes.id === 'a') capture = true; };
parser.ontext = (text) => { if (capture) console.log(text); };
parser.write('<ul><li id="a">1</li><li id="b">2</li></ul>');

xml2js 通过对象路径访问,结构固定。

// xml2js: 对象路径访问
const parser = new xml2js.Parser();
parser.parseString('<ul><li id="a">1</li><li id="b">2</li></ul>', (err, result) => {
  // 注意:xml2js 通常将标签名作为 key,属性在 $ 中
  const item = result.ul.li.find(i => i.$.id === 'a')[0]._;
});

⚡ 性能与内存:流式处理 vs 完整加载

当处理大文件时,内存管理至关重要。

cheerio 必须加载整个文档,大文件会导致内存峰值。

// cheerio: 一次性加载全部
const $ = cheerio.load(largeHtmlString); // 整个字符串在内存中

htmlparser2 支持流式写入,适合大文件。

// htmlparser2: 分块写入
const parser = new htmlparser2.Parser({...});
stream.on('data', chunk => parser.write(chunk)); // 逐块处理

sax 也是流式的,且开销更小,但仅限 XML。

// sax: 低开销流
const parser = sax.parser(true);
stream.on('data', chunk => parser.write(chunk));

xml2js 默认需要完整字符串,但也可配合流使用(较复杂)。

// xml2js: 通常处理完整字符串
parser.parseString(largeXmlString, callback); // 需确保内存足够

⚠️ 错误处理与容错性

HTML 通常是不规范的,而 XML 必须严格合规。

cheerio 和 htmlparser2 对 HTML 容错性很强,能处理闭合标签缺失等问题。

// cheerio/htmlparser2: 容忍错误标签
const $ = cheerio.load('<div><p>Unclosed'); // 能正常解析

sax 和 xml2js 对 XML 严格,格式错误会直接抛出异常。

// sax/xml2js: 严格模式
parser.onerror = (err) => { console.error('Invalid XML'); }; // 必须捕获

🌐 实际场景推荐

场景 1:编写网络爬虫

  • ✅ 最佳选择:cheerio
  • 理由:网页 HTML 通常不规范,且需要灵活提取数据,CSS 选择器最高效。
// 爬虫示例
const $ = cheerio.load(html);
const title = $('h1').text();

场景 2:解析大型日志文件(XML 格式)

  • ✅ 最佳选择:sax 或 htmlparser2
  • 理由:文件太大不能一次性加载,流式处理更稳定。
// 流式解析
parser.write(chunk);

场景 3:读取配置文件(pom.xml, web.config)

  • ✅ 最佳选择:xml2js
  • 理由:配置文件结构固定,转为对象后访问更方便。
// 配置读取
const config = result.project.properties[0];

场景 4:自定义服务端渲染引擎

  • ✅ 最佳选择:htmlparser2
  • 理由:需要拦截标签并注入逻辑,事件回调最灵活。
// 自定义逻辑
onopentag: (name) => { if(name === 'template') inject(); }

📊 核心差异总结

特性cheeriohtmlparser2saxxml2js
主要用途HTML 抓取/DOM 操作高性能 HTML/XML 解析严格 XML 流解析XML 转 JS 对象
API 风格jQuery 选择器事件回调事件回调回调/Promise
内存模式完整加载流式/完整流式完整加载为主
容错性高(容忍 HTML 错误)高低(严格 XML)低(严格 XML)
学习成本低中高低

💡 最终建议

选择工具时,先看数据格式,再看处理方式。

  • 如果是 HTML 且需要 提取数据:直接用 cheerio,开发最快。
  • 如果是 HTML 且文件 非常大:用 htmlparser2 流式处理。
  • 如果是 XML 且需要 转为对象:用 xml2js,最省事。
  • 如果是 XML 且需要 严格控制内存:用 sax,最底层。

注意:sax 和 xml2js 仅适用于 XML,处理 HTML 可能会出错。cheerio 虽然支持部分 XML,但主要针对 HTML 优化。在现代项目中,如果追求极致性能且处理 XML,也可以考虑 fast-xml-parser 作为 xml2js 的替代,但在本对比范围内,xml2js 仍是对象转换的标准选择。

如何选择: cheerio vs htmlparser2 vs sax vs xml2js

  • cheerio:

    如果你需要像在前端一样使用 jQuery 语法(如 $('selector'))来抓取或操作 HTML 内容,选择 cheerio。它最适合编写网络爬虫或处理服务端渲染的 HTML 片段,因为它提供了熟悉的 DOM 查询接口,开发效率最高。

  • htmlparser2:

    如果你需要极高的解析性能,或者需要处理非常大的 HTML/XML 文件而不想一次性加载到内存,选择 htmlparser2。它支持流式处理,允许你通过事件回调自定义解析逻辑,适合构建自定义的模板引擎或数据提取器。

  • sax:

    如果你需要严格遵循 XML 标准进行流式解析,且对内存占用非常敏感,选择 sax。它是一个底层的 SAX 风格解析器,适合处理严格的 XML 数据流,但需要你自己编写较多代码来处理节点事件。

  • xml2js:

    如果你的主要目标是将 XML 数据直接转换为 JavaScript 对象(JSON 结构)以便后续处理,选择 xml2js。它非常适合处理配置文件、SOAP 消息或任何需要将 XML 层级结构映射为对象树的场景,省去了手动遍历的麻烦。

cheerio的README

cheerio

The fast, flexible, and elegant library for parsing and manipulating HTML and XML.

中文文档 (Chinese Readme)

import * as cheerio from 'cheerio';
const $ = cheerio.load('<h2 class="title">Hello world</h2>');

$('h2.title').text('Hello there!');
$('h2').addClass('welcome');

$.html();
//=> <html><head></head><body><h2 class="title welcome">Hello there!</h2></body></html>

Installation

Install Cheerio using a package manager like npm, yarn, or bun.

npm install cheerio
# or
bun add cheerio

Features

❤ Proven syntax: Cheerio implements a subset of core jQuery. Cheerio removes all the DOM inconsistencies and browser cruft from the jQuery library, revealing its truly gorgeous API.

ϟ Blazingly fast: Cheerio works with a very simple, consistent DOM model. As a result parsing, manipulating, and rendering are incredibly efficient.

❁ Incredibly flexible: Cheerio wraps around parse5 for parsing HTML and can optionally use the forgiving htmlparser2. Cheerio can parse nearly any HTML or XML document. Cheerio works in both browser and server environments.

API

Loading

First you need to load in the HTML. This step in jQuery is implicit, since jQuery operates on the one, baked-in DOM. With Cheerio, we need to pass in the HTML document.

// ESM or TypeScript:
import * as cheerio from 'cheerio';

// In other environments:
const cheerio = require('cheerio');

const $ = cheerio.load('<ul id="fruits">...</ul>');

$.html();
//=> <html><head></head><body><ul id="fruits">...</ul></body></html>

Selectors

Once you've loaded the HTML, you can use jQuery-style selectors to find elements within the document.

$( selector, [context], [root] )

selector searches within the context scope which searches within the root scope. selector and context can be a string expression, DOM Element, array of DOM elements, or cheerio object. root, if provided, is typically the HTML document string.

This selector method is the starting point for traversing and manipulating the document. Like in jQuery, it's the primary method for selecting elements in the document.

$('.apple', '#fruits').text();
//=> Apple

$('ul .pear').attr('class');
//=> pear

$('li[class=orange]').html();
//=> Orange

Rendering

When you're ready to render the document, you can call the html method on the "root" selection:

$.root().html();
//=>  <html>
//      <head></head>
//      <body>
//        <ul id="fruits">
//          <li class="apple">Apple</li>
//          <li class="orange">Orange</li>
//          <li class="pear">Pear</li>
//        </ul>
//      </body>
//    </html>

If you want to render the outerHTML of a selection, you can use the outerHTML prop:

$('.pear').prop('outerHTML');
//=> <li class="pear">Pear</li>

You may also render the text content of a Cheerio object using the text method:

const $ = cheerio.load('This is <em>content</em>.');
$('body').text();
//=> This is content.

The "DOM Node" object

Cheerio collections are made up of objects that bear some resemblance to browser-based DOM nodes. You can expect them to define the following properties:

  • tagName
  • parentNode
  • previousSibling
  • nextSibling
  • nodeValue
  • firstChild
  • childNodes
  • lastChild

Screencasts

https://vimeo.com/31950192

This video tutorial is a follow-up to Nettut's "How to Scrape Web Pages with Node.js and jQuery", using cheerio instead of JSDOM + jQuery. This video shows how easy it is to use cheerio and how much faster cheerio is than JSDOM + jQuery.

Cheerio in the real world

Are you using cheerio in production? Add it to the wiki!

Sponsors

Does your company use Cheerio in production? Please consider sponsoring this project! Your help will allow maintainers to dedicate more time and resources to its development and support.

Headlining Sponsors

Tidelift Github AirBnB HasData

Other Sponsors

OnlineCasinosSpelen Nieuwe-Casinos.net

Backers

Become a backer to show your support for Cheerio and help us maintain and improve this open source project.

Vasy Kafidoff

License

MIT