# 从 Node 批量运行 Lighthouse 审计数百个页面（无需 PageSpeed API 密钥），以及分数为何会波动

**URL:** <https://discuss.run/t/topic/182>\
**Category:** 开发运维\
**Tags:** seo, node, javascript, webperf\
**Created:** [2026 年10 月 6 日 04:52 UTC](https://discuss.run/t/topic/182 "2026-10-06T04:52:50Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![swiftkit\_dev](https://discuss.run/letter_avatar_proxy/v4/letter/s/f19dbf/32.png) [@swiftkit\_dev](https://discuss.run/u/swiftkit_dev)\
**Post date:** [2026 年10 月 6 日 04:52 UTC](https://discuss.run/t/topic/182/1 "2026-10-06T04:52:50Z")

</div>

> 来源：devto:articles · 原作者 SwiftKit · [原文](https://dev.to/swiftkit_dev/run-lighthouse-on-hundreds-of-pages-from-node-no-pagespeed-api-key-and-why-the-scores-move-2dk4)  
> 系统转载，非作者本人发布。

PageSpeed Insights 非常适合单个 URL。但面对 300 个产品页，或者“每次发布后所有落地页”这样的场景，你需要的是在脚本里运行 Lighthouse。好消息是：Lighthouse 是一个 npm 包，你不需要 API 密钥就能自己运行它。

## 最小脚本

```js
import lighthouse from 'lighthouse';
import * as chromeLauncher from 'chrome-launcher';

const chrome = await chromeLauncher.launch({
  chromeFlags: ['--headless=new', '--no-sandbox', '--disable-dev-shm-usage'],
});
try {
  const { lhr, report } = await lighthouse('https://www.wikipedia.org', {
    port: chrome.port,
    output: ['json', 'html'],
    onlyCategories: ['performance', 'accessibility', 'best-practices', 'seo'],
    logLevel: 'error',
  });
  console.log(Math.round(lhr.categories.performance.score * 100));
  // report[1] is the full HTML report: save it, it's what people actually read.
} finally {
  await chrome.kill();
}

```

对于桌面预设，将 Lighthouse 自身的配置作为第三个参数传入：

```js
const desktop = (await import('lighthouse/core/config/desktop-config.js')).default;
await lighthouse(url, flags, desktop);

```

我遇到的两个小坑：

- 在 chrome-launcher 1.x 中，`kill()` 不保证返回 promise，所以 `chrome.kill().catch(...)` 可能会抛错。改用 try/catch 包裹它。
- 如果你在 Docker 中使用 Playwright 镜像，将 chrome-launcher 指向 Playwright 的浏览器：`chromePath: chromium.executablePath()`。

## 从结果中保留什么

`lhr` 对象非常庞大。对于批量表格，我每页保留以下内容：

```js
const score = (c) => Math.round(lhr.categories[c].score * 100);
const ms = (id) => Math.round(lhr.audits[id].numericValue);
const row = {
  performance: score('performance'),
  seo: score('seo'),
  lcpMs: ms('largest-contentful-paint'),
  tbtMs: ms('total-blocking-time'),
  cls: lhr.audits['cumulative-layout-shift'].numericValue,
  // Top fixes, ranked by time saved:
  opportunities: Object.values(lhr.audits)
    .filter((a) => a.details?.type === 'opportunity' && a.details.overallSavingsMs >= 100)
    .sort((a, b) => b.details.overallSavingsMs - a.details.overallSavingsMs)
    .slice(0, 8)
    .map((a) => ({ title: a.title, savingsMs: Math.round(a.details.overallSavingsMs) })),
};

```

在读取任何内容之前，也要检查 `lhr.runtimeError`。加载失败的页面仍会返回 `lhr`，只是分数是无效的。

## 一次只运行一个审计

同时审计 10 个页面很诱人。但别这样做：Lighthouse 会模拟慢速设备并测量 CPU 工作量，因此在同一台机器上并行审计会互相拖慢，并把每个性能分数都拉低。请逐个运行，每次都用全新的 Chrome。这样虽然更慢，但数字才有意义。

## 分数为何会波动

我在自己的笔记本和一个 2 GB 云容器上运行了相同的页面：

| 页面 | 笔记本（移动端） | 云容器（移动端） |
| --- | --- | --- |
| [wikipedia.org](http://wikipedia.org) 性能 | 98 | 92 |
| [wikipedia.org](http://wikipedia.org) LCP | 1,379 ms | 1,497 ms |

一个更重的营销网站在云端的性能得分仅为 **25** ，LCP 超过 9 秒。同样的代码，同一天。

实验室分数取决于机器的 CPU，而移动端预设会在此基础上叠加 CPU 降速。因此：

- **在同一台机器、同一次运行中比较页面** ，不要跨工具比较。
- PageSpeed Insights 在 Google 拥有足够流量时也会显示 **真实用户数据** （CrUX）。这是一种不同的测量，而且往往对 SEO 更关键。
- 追踪 **趋势** （相同页面、相同配置，每周一次），而不是单个数字。

## 打包好的版本

如果你不想自己运行 Chrome 集群，我把它放到了 Apify 上，名为 **[Lighthouse Audit](https://apify.com/swiftkit/lighthouse-audit)**：粘贴 URL，选择移动端、桌面端或两者，即可获得分数、Core Web Vitals、最重要的修复建议、失败的 SEO/无障碍检查，以及每个页面的完整 HTML 报告。出于上述原因，它一次只运行一个审计。失败的页面不收费。

你们目前是如何跨多个页面追踪性能的？我很好奇大家是使用 CrUX 数据、实验室运行，还是两者都用。

* * *

_在 AI 辅助下撰写；代码和数字在发布前均已测试。_

---

<div class="post-metadata">

**Author:** ![beusebiu](https://discuss.run/letter_avatar_proxy/v4/letter/b/db5fbb/32.png) [@beusebiu](https://discuss.run/u/beusebiu)\
**Post date:** [2026 年10 月 6 日 04:52 UTC](https://discuss.run/t/topic/182/2 "2026-10-06T04:52:50Z")

</div>

## 原站讨论节选 · Eusebiu Balan

> 原站发表于 2026-10-03T23:29:29.000Z  
> 原站评论节选，非作者本人在本站回复。[原评论](https://dev.to/comment/3gd38)

那个表格里也值得保留 lhr.environment.benchmarkIndex。Lighthouse 会在每次运行前测量机器的速度，因此当每周分数下降时，你可以判断是容器变慢了还是页面变慢了。

他们关于波动性的说明还建议每个 URL 运行多次并取中位数，这能平滑掉繁忙机器上一次偶发的糟糕运行。

---

<div class="post-metadata">

**Author:** ![swiftkit\_dev](https://discuss.run/letter_avatar_proxy/v4/letter/s/f19dbf/32.png) [@swiftkit\_dev](https://discuss.run/u/swiftkit_dev)\
**Post date:** [2026 年10 月 6 日 04:52 UTC](https://discuss.run/t/topic/182/3 "2026-10-06T04:52:50Z")

</div>

## 原站讨论节选 · SwiftKit

> 原站发表于 2026-10-04T02:04:36.000Z · 回复原评论 3gd38  
> 原站评论节选，非作者本人在本站回复。[原评论](https://dev.to/comment/3gd5i)

谢谢，这是个很好的建议。我已经把这两点都加到了工具里：每条结果现在都包含 `benchmarkIndex`，并且有一个“每页运行次数”选项（最多 5 次），会保留性能分数为中位数的那次运行，同时列出所有分数和每次运行的 `benchmarkIndex`。在一次快速测试中，三次运行分别得到 97、100 和 100，benchmark 指数分别为 3963、3834 和 4115，这很好地说明了机器本身在运行之间变化有多大。
