返回博客
指南
Sergiu InizianLast updated on May 8, 20263 min read

Cheerio 与 Puppeteer:如何选择正确的工具

Cheerio 与 Puppeteer:如何选择正确的工具
简而言之:Cheerio 是一个轻量级的 HTML 解析器;Puppeteer 则驱动真正的 Chromium 浏览器。当数据已以原始 HTML 形式存在时,使用 Cheerio;当数据由 JavaScript 渲染时,使用 Puppeteer;若遇到 JavaScript 内容繁多的页面,且每次访问需提取大量字段,则将两者结合使用。

如果你正在开发一个 Node.js 爬虫,当目标网站首次停止配合时,通常就会面临“Cheerio 还是 Puppeteer”的选择难题。也许 Cheerio 在 React 页面上返回了空选择器,或者 Puppeteer 在本应只需几毫秒的任务上却占用了大量 CPU 资源。这两个库之所以流行自有其道理,但对于用户交给它们的任务,有一半情况它们都不适用。

Cheerio 是一个具有 jQuery 风格 API 的服务器端 HTML 解析器。Puppeteer 则是无头 Chromium 的控制器。在选择 Cheerio 还是 Puppeteer 时,最清晰的思维模型是将渲染(将 JavaScript 转换为最终的 DOM)与解析(从该 DOM 中提取字段)区分开来。

本指南将介绍每个库的工作原理、各自的适用场景、能够处理大多数真实网站的混合模式、一个可运行的报价抓取示例,以及一旦离开 localhost 环境就必须应对的反机器人现实。

Cheerio 与 Puppeteer 对比一览

两者都是位于不同层级的 Node.js 库。Cheerio 基于你已有的 HTML 提供选择器来提取数据。Puppeteer 则启动一个真正的 Chromium 浏览器,运行页面的 JavaScript,并允许你读取或点击结果。

区分 Cheerio 和 Puppeteer 最快捷的方式是“查看源代码”测试:在原始源代码中搜索你想要的数据。如果数据存在,Cheerio 配合 HTTP 客户端就足够了。如果源代码只是包含 JS 包的空壳,你就需要浏览器。

Cheerio 的底层工作原理

Cheerio 是一个仅用于解析的库。你向它传入一个 HTML 字符串,其中包含 cheerio.load(html) ,它便会返回一个 $ 一个模拟 jQuery 的对象: $('h1').text(), $('a.product').each(...),支持属性查找和遍历。它不包含 DOM、布局引擎或 JavaScript 运行时。

Cheerio 也不会自行获取 HTML,因此你需要将其与 axios、node-fetch 或 undici 配合使用。这种分离正是其特色:你掌控请求层,而 Cheerio 专注于解析。

Puppeteer 的底层工作原理

Puppeteer 是一个 Node.js API,它通过 Chrome DevTools 协议控制 Chrome 或 Chromium。当你 npm install puppeteer,它会附带一个匹配的 Chromium 构建版本,因此你无需单独管理浏览器二进制文件。

典型的脚本遵循相同的生命周期: puppeteer.launch(), browser.newPage(), page.goto(url), page.waitForSelector(...) 处理异步内容,然后 page.content()page.evaluate(...) 来读取渲染后的 DOM。

功能对比

将 Cheerio 与 Puppeteer 并列比较,与其说是为了分出胜负,不如说是为了确认你需要完成什么任务。在速度和资源占用方面,Cheerio 更胜一筹。而 JavaScript 渲染、网络拦截和用户模拟功能则属于 Puppeteer。

功能

Cheerio

Puppeteer

主要用途

解析 HTML/XML

自动化 Chromium

JavaScript 执行

浏览器依赖

捆绑版 Chromium

每页加载速度

毫秒

每个工作线程的内存

几 MB

每个浏览器数百MB

点击、表单、滚动

屏幕截图和PDF

学习曲线

简单(jQuery/CSS)

较陡(异步、生命周期)

典型用例

静态、服务器端渲染

单页应用(SPA)、仪表盘、登录页面

性能与资源占用

Cheerio 能在几毫秒内解析 HTML,且每个 worker 仅占用几兆字节内存,因此数千次解析可容纳在一个 Node.js 进程或小型无服务器函数中。每个 Puppeteer 浏览器实例通常需要 150 至 300 MB 的内存(本文撰写时的社区基准数据;请根据您的工作负载进行验证)。在 1 GB 的 Lambda 实例上,这会很快限制并行处理能力。

学习曲线与开发者体验

只要你会编写 $('div.card .price').text(),就能编写 Cheerio。大多数任务只需 10 到 20 行代码。而 Puppeteer 则要求更多:需要熟悉 async/await、页面生命周期,以及调试因数据未加载而导致选择器过早触发的定时错误。

何时选择 Cheerio

当响应体中已包含所需数据时,请选用 Cheerio。这涵盖了公共网络的绝大部分场景:博客、新闻、营销页面、RSS、网站地图、服务器渲染的搜索结果以及文档。

在针对这些目标的 Cheerio 与 Puppeteer 对比中,Puppeteer 实属大材小用。Cheerio 配合 axios 的管道方案每秒能抓取更多页面,且能适配低成本的无服务器 worker。如果 curl URL | grep 能找到数据,Cheerio 也能。

何时应选择 Puppeteer

当页面更像应用程序而非文档时,Puppeteer 便能发挥其价值。基于 React、Vue 或 Angular 构建的单页应用(SPA)通常会先加载一个空壳,并在加载完成后通过 XHR 注入内容。此外,对于无限滚动、需登录的仪表盘、Cookie 或会话处理以及多步骤表单,您也需要使用真正的浏览器。

当抓取只是更广泛自动化任务的副产品时,Puppeteer 同样适用:例如截图、生成 PDF、表单提交以及广告位检测。

混合方案:Puppeteer 渲染,Cheerio 解析

对于大多数非简单网站,关于 Cheerio 与 Puppeteer 的最佳解决方案是两者结合。将渲染解析视为独立的任务:Puppeteer 构建最终的 DOM,然后将 HTML 传递给 Cheerio。

为何要分离?运行 querySelectorAllpage.evaluate 会为每次选择器遍历向浏览器发送回调,而每次往返都会穿越 DevTools 协议。对于横跨千页的十个字段,成本会呈指数级累积。

使用 page.content()使用 Cheerio 在本地解析,速度更快,且您的选择器逻辑在无需浏览器的情况下仍可调试。

实战指南:使用 Puppeteer + Cheerio 抓取报价

npm init -y && npm install puppeteer cheerio,针对 quotes.toscrape.com 的混合抓取器看起来像这样:

const puppeteer = require('puppeteer');
const cheerio = require('cheerio');

(async () => {
  const browser = await puppeteer.launch({ headless: 'new' });
  const page = await browser.newPage();
  await page.goto('https://quotes.toscrape.com/', { waitUntil: 'networkidle2' });

  const html = await page.content();
  await browser.close();

  const $ = cheerio.load(html);
  const quotes = [];
  $('div.quote').each((_, el) => {
    quotes.push({
      quote: $(el).find('span.text').text().trim(),
      author: $(el).find('.author').text().trim(),
    });
  });
  console.log(quotes);
})();

page.content() 返回当前文档的序列化 HTML,这是旧版 page.evaluate(() => document.documentElement.innerHTML) 模式的更简洁现代替代方案。Cheerio 代码块与 axios 获取 HTML 后的处理方式完全一致。

扩展部署:代理、反机器人防御与可靠性

在 localhost 上,每个爬虫都能正常工作。但在生产环境中,一旦超过速率阈值,故障模式就会以 403、429 错误、指纹识别挑战和 CAPTCHA 的形式显现。仅运行 Puppeteer 并非隐蔽的解决方案:原生无头 Chromium 会发出可被检测到的信号。

真正的扩展方案包括轮换使用住宅或移动代理、随机化用户代理、采用退避策略重试,并将更复杂的请求路由至托管式爬取 API——该 API 会返回渲染后的 HTML 供 Cheerio 使用。

值得了解的 Cheerio 和 Puppeteer 替代方案

这两款库并非唯一选择。Playwright 与 Puppeteer 类似,但支持 Chromium、Firefox 和 WebKit,其自动等待功能能有效解决许多时间同步难题。Selenium 是历史悠久的多平台工作马,若您的团队已运行自动化测试集群,它便是理想之选。在解析方面,jsdom 可提供真实的 DOM 并执行脚本,而 htmlparser2 则是 Cheerio 构建所基于的流式解析器。

关键要点

  • 渲染与解析是不同的任务。Cheerio 负责解析 HTML;Puppeteer 负责渲染页面。应根据任务需求选择工具,而非受制于个人习惯。
  • 使用“查看源代码”测试法。如果数据位于原始响应中,Cheerio 配合 HTTP 客户端会更快、更经济且更易于维护。
  • 当 DOM 由 JavaScript 构建、需要点击、滚动或登录操作,或者页面是 SPA 壳时,请选用 Puppeteer
  • 在包含大量字段且 JavaScript 代码繁重的页面上,混合模式更胜一筹。先使用 page.content(),随后在 Node 中使用 Cheerio 解析 HTML。
  • 尽早规划反机器人防御措施。代理、指纹加固、重试机制和验证码将决定你的爬虫能否在生产环境中存活。

常见问题

Cheerio 真的比 Puppeteer 快吗?快多少?

是的,Cheerio 速度快得多,因为它根本不启动浏览器。Cheerio 的解析仅需几毫秒即可完成内存中字符串的处理,而 Puppeteer 的页面加载则受限于网络、渲染和 JavaScript 执行时间,通常需要几秒钟。在同等硬件条件下,通常可以并行运行比 Puppeteer 多 10 到 100 倍的 Cheerio 工作线程。

Cheerio 能抓取 JavaScript 渲染的页面或单页应用吗?

不能。Cheerio 只能解析您提供的 HTML 内容,因此如果页面通过客户端 JavaScript 生成内容,Cheerio 返回的将是空壳。您需要在 Cheerio 前端使用 Puppeteer 或 Playwright 等无头浏览器,或者有时可以直接调用 SPA 调用的底层 JSON API,从而完全跳过渲染过程。

使用 Cheerio 是否需要 axios 或 node-fetch?

你需要某个 HTTP 客户端,但不一定非得是 axios。Cheerio 期望接收一个 HTML 字符串,因此任何能获取该字符串的工具均可使用:axios、node-fetch、undici、现代 Node.js 中的内置 fetch ,甚至读取已保存的 .html 文件。请选择能满足项目所需重试、代理和头部控制功能的客户端。

如果我已经在测试中使用 Puppeteer,它能否完全替代 Cheerio?

从技术上讲可以,因为 Puppeteer 可以通过 page.$$eval 等辅助函数查询 DOM。但在实际应用中这很浪费:每次选择器遍历都会调用 DevTools 协议,且你需要为那些本不需要 JavaScript 的任务承担浏览器的内存开销。大多数团队会保留 Puppeteer 用于渲染,并让 Cheerio 负责数据提取。

对于全新的爬取项目,我应该选择 Puppeteer 还是 Playwright?

如今,Playwright 通常是更稳妥的默认选择。它通过单一 API 支持 Chromium、Firefox 和 WebKit,具备更完善的自动等待功能,并且开箱即用时就拥有更强大的调试工具。如果您的团队已具备 Puppeteer 专业知识、仅针对 Chromium 进行开发,或者正在扩展现有的 Puppeteer 代码库,则应选择 Puppeteer。

结论

Cheerio 与 Puppeteer 的抉择,本质上取决于目标页面是否需要浏览器才能存在。如果 HTML 响应中已包含数据,那么 Cheerio 配合 HTTP 客户端便是轻量、快速且经济的解决方案。如果 JavaScript 框架在运行时组装 DOM,则需要先使用 Puppeteer 或 Playwright 进行渲染。 当页面包含大量 JavaScript 代码,但您需要从每次访问中提取大量数据时,建议使用 Puppeteer 渲染一次,再通过 Cheerio 进行解析。

没有任何库能单独解决生产环境中的实际问题:访问封锁、IP轮换、指纹识别和验证码。如果你希望保留 Cheerio 代码并避免在请求层与这些障碍抗争,WebScrapingAPI 可以通过单一接口返回已渲染且未受封锁的 HTML,这样你的解析器就可以专注于选择器,而非隐藏访问。 选择适合目标页面的工具,并尽早规划反机器人防御措施,这样你将大幅减少调试爬虫的时间,从而有更多时间利用数据。

关于作者
Sergiu Inizian, 技术内容撰稿人 @ WebScrapingAPI
Sergiu Inizian技术内容撰稿人

Sergiu Inizian 是 WebScrapingAPI 的技术内容撰稿人,负责创作清晰、实用的内容,帮助开发者了解产品并有效使用它。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网络数据。