使用无头 Chrome 将 MHTML 归档渲染为 PDF,尽量贴近真实浏览器的加载与打印结果。
这是一个基于 Node.js 的命令行工具。输入一个 .mhtml 文件,工具会启动 Chromium/Chrome 打开该归档,再调用浏览器的 PDF 导出能力生成目标文件。
适合以下场景:
- 将浏览器保存的 MHTML 网页归档转换成 PDF。
- 尽量保留页面的真实样式、字体、背景图和打印布局。
- 在脚本、自动化任务或批处理流程中调用命令行完成转换。
本项目在 package.json 中声明了两个直接依赖:
puppeteer- 负责启动 Chromium/Chrome、打开 MHTML 文件并导出 PDF。
- 默认情况下,安装依赖时 Puppeteer 通常会准备一份可用的浏览器二进制。
commander- 负责解析命令行参数,例如输入/输出路径、纸张大小、超时时间、边距等。
建议使用 Node.js 18 或更高版本。
原因:
- 当前锁定的
puppeteer版本为22.15.0。 - 当前锁定的
commander版本为12.1.0。 - 这套依赖组合更适合在较新的 Node.js 环境中运行。
可先确认本机版本:
node -v
npm -v本工具本质上依赖 Chromium 内核来渲染 MHTML,因此必须满足以下任一条件:
- 安装项目依赖时,由 Puppeteer 自动准备可用的 Chromium。
- 机器上已经安装了 Chrome/Chromium,并在运行命令时通过
--chrome-executable指定路径。
如果你希望复用系统已有浏览器,可使用:
npx mhtml2pdf input.mhtml output.pdf --chrome-executable /path/to/chrome在 macOS 上,也支持直接传入 .app:
npx mhtml2pdf input.mhtml output.pdf --chrome-executable "/Applications/Google Chrome.app"程序会自动解析为应用包内真正的可执行文件。
虽然本仓库本身很小,但实际渲染是否成功还取决于运行环境:
- 需要允许启动 Chromium/Chrome。
- 如果 MHTML 中引用了外部网络资源,运行时需要网络访问能力。
- 某些 CI、容器、远程沙箱或权限受限环境可能会拦截浏览器启动。
根据你的使用方式,通常有两种安装/运行场景。
适合开发、调试或本地直接运行当前仓库代码。
- 安装依赖:
npm install- 运行 CLI:
node bin/mhtml2pdf.js <input.mhtml> <output.pdf> [options]也可以通过包名方式运行当前目录已安装的可执行脚本:
npx --no-install mhtml2pdf <input.mhtml> <output.pdf> [options]如果后续希望在任意目录都能直接调用 mhtml2pdf,可以全局安装:
npm install -g .安装后可直接执行:
mhtml2pdf <input.mhtml> <output.pdf> [options]如果你只是临时在当前项目目录内执行,不想全局安装,则优先使用上面的源码目录运行方式。
npx mhtml2pdf <input.mhtml> <output.pdf> [options]参数说明:
<input.mhtml>:输入的 MHTML 文件路径。<output.pdf>:输出的 PDF 文件路径;如果目标目录不存在,程序会自动创建。
npm install
node bin/mhtml2pdf.js ./samples/page.mhtml ./out/page.pdf或者:
npm install
npx --no-install mhtml2pdf ./samples/page.mhtml ./out/page.pdf仓库中还定义了一个脚本:
"scripts": {
"convert": "node bin/mhtml2pdf.js"
}因此也可以这样执行:
npm run convert -- ./input.mhtml ./output.pdf注意:
--之后的内容才会传给 CLI。- 这种方式本质上仍然是执行
node bin/mhtml2pdf.js。
npx mhtml2pdf ./page.mhtml ./page.pdfnpx mhtml2pdf ./page.mhtml ./page.pdf --format A4 --margin 10mm,15mm,10mm,15mmnpx mhtml2pdf ./page.mhtml ./page.pdf --landscapenpx mhtml2pdf ./page.mhtml ./page.pdf --wait-until networkidle0 --timeout 60000npx mhtml2pdf ./page.mhtml ./page.pdf --chrome-executable "/Applications/Google Chrome.app"npx mhtml2pdf ./page.mhtml ./page.pdf --launch-arg="--no-sandbox" --launch-arg="--disable-dev-shm-usage"--format <format>:纸张尺寸,默认A4。--scale <ratio>:缩放比例,范围0.1-2,默认1。--timeout <ms>:页面加载超时时间,默认60000毫秒。--wait-until <event>:等待的生命周期事件,可选load、domcontentloaded、networkidle0、networkidle2,默认networkidle0。--landscape:横向输出 PDF。--no-background:不打印背景图和背景色。--margin <values>:PDF 边距,支持单值或四值,例如10mm或10mm,15mm,10mm,15mm。--chrome-executable <path>:指定 Chrome/Chromium 可执行文件路径;macOS 支持直接传.app。--launch-arg <flag ...>:追加 Chromium 启动参数,可多次传入。
程序的大致工作过程如下:
- 校验输入文件是否存在且可读取。
- 解析输入/输出路径,并自动创建输出目录。
- 启动 Puppeteer 管理的 Chromium/Chrome。
- 自动附加本地文件访问相关启动参数,使 MHTML 中的本地引用更容易正常工作。
- 通过
file://URL 打开输入的.mhtml文件。 - 等待指定生命周期事件完成。
- 使用浏览器 PDF 导出能力生成输出文件。
- 关闭浏览器进程。
通常是因为 Puppeteer 在准备浏览器二进制。这是正常现象之一。
如果你不想依赖 Puppeteer 下载浏览器,可以改为使用系统已有的 Chrome/Chromium,并在运行时传入:
--chrome-executable /path/to/chrome如果报错路径指向 ~/.npm/_npx/... 或 ~/.npm/_logs/...,通常是 npm 缓存目录权限异常。
可选处理方式:
- 修复
~/.npm的 owner/权限。 - 临时使用项目内缓存目录:
npm_config_cache=$PWD/.npm-cache npx --no-install mhtml2pdf ./input.mhtml ./output.pdf这通常不是业务代码错误,而是运行环境限制导致,例如:
- 沙箱环境禁止启动浏览器。
- 容器缺少必要系统能力。
- 某些远程环境限制 GUI/浏览器相关进程。
此时建议优先在本机正常终端中重试同一命令,或者显式指定本机可用的 Chrome:
npx mhtml2pdf ./input.mhtml ./output.pdf --chrome-executable "/Applications/Google Chrome.app"可能原因包括:
- MHTML 本身保存时就不完整。
- 页面引用了外部资源,但运行环境无法联网。
- 目标页面依赖某些 Chromium 行为,而归档恢复并不完全等同真实在线站点。
可尝试:
- 增加超时时间:
--timeout 120000 - 调整等待事件:
--wait-until load或--wait-until networkidle2 - 指定本机 Chrome/Chromium 以减少内核差异
如果你是仓库维护者或准备修改代码,最直接的启动方式是:
npm install
npm run convert -- ./input.mhtml ./output.pdfCLI 入口文件位于:
bin/mhtml2pdf.js
包信息位于:
package.json