乐闻世界logo
搜索文章和话题

Cheerio 和 jsdom 有什么区别?如何选择使用?

2月22日 14:31

Cheerio 和 jsdom 都是 Node.js 中处理 HTML/XML 的工具,但它们的设计理念和实现方式有显著差异。以下是详细的对比分析:

1. 核心架构对比

Cheerio

  • 类型:HTML 解析器
  • 底层实现:基于 htmlparser2
  • DOM 实现:自定义的轻量级 DOM 实现
  • JavaScript 执行:不支持
  • 浏览器环境模拟:不模拟

jsdom

  • 类型:完整的 DOM 和浏览器环境模拟器
  • 底层实现:基于 WHATWG DOM 标准
  • DOM 实现:完整的 W3C DOM 规范实现
  • JavaScript 执行:完全支持
  • 浏览器环境模拟:完整模拟

2. 功能对比表

特性Cheeriojsdom
HTML 解析✅ 快速✅ 标准
CSS 选择器✅ jQuery 风格✅ 标准
DOM 操作✅ 基础操作✅ 完整 API
JavaScript 执行❌ 不支持✅ 完全支持
事件处理❌ 不支持✅ 完全支持
性能⚡ 极快🐢 较慢
内存占用📉 低📈 高
浏览器 API❌ 无✅ 完整
网络请求❌ 无✅ 支持
Canvas❌ 无✅ 支持
LocalStorage❌ 无✅ 支持

3. 使用示例对比

Cheerio 使用示例

javascript
const cheerio = require('cheerio'); const html = ` <div id="container"> <p class="text">Hello World</p> <button onclick="alert('Clicked')">Click</button> </div> `; const $ = cheerio.load(html); // 基本操作 console.log($('#container').text()); // "Hello World" console.log($('.text').text()); // "Hello World" // DOM 操作 $('.text').addClass('highlight'); console.log($('.text').attr('class')); // "text highlight" // 无法执行 JavaScript $('button').click(); // 无效,Cheerio 不支持事件

jsdom 使用示例

javascript
const { JSDOM } = require('jsdom'); const html = ` <div id="container"> <p class="text">Hello World</p> <button onclick="alert('Clicked')">Click</button> </div> `; const dom = new JSDOM(html); const document = dom.window.document; // 基本操作 console.log(document.getElementById('container').textContent); // "Hello World" console.log(document.querySelector('.text').textContent); // "Hello World" // DOM 操作 document.querySelector('.text').classList.add('highlight'); console.log(document.querySelector('.text').className); // "text highlight" // 可以执行 JavaScript const button = document.querySelector('button'); button.click(); // 有效,会触发 onclick 事件 // 可以使用浏览器 API console.log(dom.window.innerWidth); // 窗口宽度 console.log(dom.window.location.href); // 当前 URL

4. 性能对比

解析速度测试

javascript
const cheerio = require('cheerio'); const { JSDOM } = require('jsdom'); const largeHtml = '<div>' + '<p>Test</p>'.repeat(10000) + '</div>'; // Cheerio 性能测试 const start1 = Date.now(); const $ = cheerio.load(largeHtml); const cheerioTime = Date.now() - start1; console.log(`Cheerio: ${cheerioTime}ms`); // jsdom 性能测试 const start2 = Date.now(); const dom = new JSDOM(largeHtml); const jsdomTime = Date.now() - start2; console.log(`jsdom: ${jsdomTime}ms`); // 典型结果: // Cheerio: 5-10ms // jsdom: 100-500ms

内存占用对比

javascript
// Cheerio - 内存占用低 function cheerioMemoryTest() { const $ = cheerio.load(largeHtml); const elements = $('p'); return elements.length; } // jsdom - 内存占用高 function jsdomMemoryTest() { const dom = new JSDOM(largeHtml); const elements = dom.window.document.querySelectorAll('p'); return elements.length; }

5. 适用场景对比

使用 Cheerio 的场景

javascript
// 1. 网页爬虫和数据提取 async function scrapeWebsite() { const axios = require('axios'); const response = await axios.get('https://example.com'); const $ = cheerio.load(response.data); return { title: $('title').text(), links: $('a').map((i, el) => $(el).attr('href')).get() }; } // 2. HTML 内容处理 function processHtml(html) { const $ = cheerio.load(html); $('script').remove(); // 移除脚本 $('style').remove(); // 移除样式 return $.html(); } // 3. 批量处理大量文档 function batchProcess(htmlList) { return htmlList.map(html => { const $ = cheerio.load(html); return $('title').text(); }); }

使用 jsdom 的场景

javascript
// 1. 测试前端代码 const { JSDOM } = require('jsdom'); function testFrontendCode() { const dom = new JSDOM(` <div id="app"></div> <script> document.getElementById('app').textContent = 'Hello'; </script> `, { runScripts: 'dangerously' }); console.log(dom.window.document.getElementById('app').textContent); } // 2. 服务端渲染 (SSR) function renderComponent(component) { const dom = new JSDOM('<div id="root"></div>'); const root = dom.window.document.getElementById('root'); // 执行组件代码 component(root); return dom.serialize(); } // 3. 处理需要 JavaScript 的内容 function processDynamicContent(html) { const dom = new JSDOM(html, { runScripts: 'dangerously', resources: 'usable' }); // 等待 JavaScript 执行完成 return new Promise(resolve => { dom.window.onload = () => { resolve(dom.serialize()); }; }); }

6. API 对比

Cheerio API 特点

javascript
const $ = cheerio.load(html); // jQuery 风格的 API $('.class').text(); $('.class').html(); $('.class').attr('href'); $('.class').addClass('active'); $('.class').find('a'); // 链式调用 $('.container') .find('.item') .addClass('highlight') .text(); // 不支持的浏览器 API $.window; // undefined $.document; // undefined $.localStorage; // undefined

jsdom API 特点

javascript
const dom = new JSDOM(html); const document = dom.window.document; // 标准 DOM API document.querySelector('.class').textContent; document.querySelector('.class').innerHTML; document.querySelector('.class').getAttribute('href'); document.querySelector('.class').classList.add('active'); document.querySelector('.class').querySelector('a'); // 支持浏览器 API dom.window.innerWidth; dom.window.location.href; dom.window.localStorage; dom.window.fetch; dom.window.console;

7. 选择建议

选择 Cheerio 的情况

  1. 只需要解析和提取数据

    • 网页爬虫
    • 数据抓取
    • HTML 内容处理
  2. 性能要求高

    • 处理大量文档
    • 批量操作
    • 实时处理
  3. 资源受限

    • 内存有限
    • CPU 有限
    • 无服务器环境
  4. 不需要浏览器功能

    • 不需要执行 JavaScript
    • 不需要事件处理
    • 不需要浏览器 API

选择 jsdom 的情况

  1. 需要完整的浏览器环境

    • 前端代码测试
    • 服务端渲染
    • 组件测试
  2. 需要执行 JavaScript

    • 动态内容处理
    • 客户端代码执行
    • 框架渲染
  3. 需要浏览器 API

    • LocalStorage
    • Fetch API
    • Canvas
    • Web Workers
  4. 需要标准 DOM 行为

    • 事件冒泡
    • DOM 事件
    • 浏览器兼容性测试

8. 混合使用场景

javascript
// 先用 jsdom 执行 JavaScript,再用 Cheerio 解析 const { JSDOM } = require('jsdom'); const cheerio = require('cheerio'); async function hybridProcess(html) { // 1. 使用 jsdom 执行 JavaScript const dom = new JSDOM(html, { runScripts: 'dangerously' }); // 等待 JavaScript 执行 await new Promise(resolve => { dom.window.onload = resolve; }); // 2. 获取执行后的 HTML const processedHtml = dom.serialize(); // 3. 使用 Cheerio 快速解析 const $ = cheerio.load(processedHtml); return { title: $('title').text(), content: $('.content').text() }; }

总结

  • Cheerio:轻量、快速、专注数据提取,适合爬虫和静态 HTML 处理
  • jsdom:完整、标准、模拟浏览器,适合测试和动态内容处理
  • 选择原则:根据需求选择,需要性能用 Cheerio,需要完整功能用 jsdom
  • 混合使用:可以结合两者优势,先用 jsdom 执行 JS,再用 Cheerio 解析
标签:NodeJSCheerio