webpage2pdf
v1.0.0
Published
Convert web pages to PDF - Command line tool and Node.js module
Maintainers
Readme
webpage2pdf
一个功能强大的工具,可以将网页、HTML 字符串、Buffer 或 Stream 转换为 PDF 文件。支持命令行和函数调用两种使用方式,还支持流式输出、多页面合并等高级功能。
安装
方式一:全局安装(推荐)
使用 npm:
npm install -g webpage2pdf或使用 pnpm:
pnpm add -g webpage2pdf之后可以直接使用:
webpage2pdf https://www.baidu.com方式二:本地安装
使用 npm:
npm install webpage2pdf
# 使用 npx 运行
npx webpage2pdf https://www.baidu.com或使用 pnpm:
pnpm add webpage2pdf
# 使用 pnpm exec 运行
pnpm exec webpage2pdf https://www.baidu.com方式三:作为依赖安装
使用 npm:
npm install webpage2pdf --save或使用 pnpm:
pnpm add webpage2pdf使用方法
方式一:命令行使用
基本用法
# 将网页转换为 PDF(默认使用页面标题作为文件名)
webpage2pdf https://www.baidu.com
# 输出:./百度一下,你就知道_202512251539.pdf
# 指定输出路径
webpage2pdf https://www.baidu.com -o ./my-pdf.pdf
# 指定页面尺寸
webpage2pdf https://www.baidu.com -s A4_PRINT
# 等待更长时间(确保页面完全加载)
webpage2pdf https://www.baidu.com -w 5000
# 等待特定元素出现
webpage2pdf https://www.baidu.com --selector "button"命令行选项
| 选项 | 简写 | 说明 | 默认值 |
|------|------|------|--------|
| --output | -o | 输出文件路径 | 使用页面标题(document.title) |
| --size | -s | 页面尺寸(A4, A4_PRINT, A3, LETTER) | A4 |
| --wait | -w | 等待时间(毫秒) | 3000 |
| --selector | | 等待的选择器(如:button, #content) | 无 |
| --header | | 自定义请求头(格式:key:value,可多次使用) | 无 |
方式二:函数调用
基本用法
const { generatePdf } = require('webpage2pdf');
// 基本使用
async function example() {
const result = await generatePdf('https://www.baidu.com', './output.pdf');
if (result.success) {
console.log('PDF 生成成功:', result.path);
console.log('文件大小:', result.size, 'bytes');
console.log('页面标题:', result.title);
} else {
console.error('生成失败:', result.error);
}
}
example();高级用法
const { generatePdf, PAGE_SIZE_CONFIG, setVerbose } = require('webpage2pdf');
// 关闭详细日志输出(函数调用时推荐)
setVerbose(false);
async function advancedExample() {
const result = await generatePdf('https://example.com', './output.pdf', {
pageSize: 'A4_PRINT', // 页面尺寸
waitTime: 5000, // 等待时间(毫秒)
selector: '#content', // 等待特定元素
headers: { // 自定义请求头
'Authorization': 'Bearer token',
'X-Custom-Header': 'value'
}
});
if (result.success) {
console.log('成功:', result.path);
}
}
advancedExample();API 文档
generatePdf(input, outputPath, options)
将网页或 HTML 转换为 PDF。
参数:
input(string|string[]|Buffer|Readable, 必需) - 输入:string- URL 或 HTML 字符串string[]- URL 数组(多页面合并)Buffer- HTML BufferReadable- HTML Stream
outputPath(string|null, 可选) - 输出文件路径string- 保存到文件null- 返回 Stream
options(object, 可选) - 配置选项pageSize(string) - 页面尺寸,可选值:A4,A4_PRINT,A3,LETTER,默认:A4waitTime(number) - 等待时间(毫秒),默认:3000selector(string) - 等待的选择器(可选),默认:nullheaders(object) - 自定义请求头(可选),默认:{}margin(object) - 页边距,格式:{top, right, bottom, left},单位 mm,默认:{top: '0mm', right: '0mm', bottom: '0mm', left: '0mm'}scale(number) - 缩放比例(0.1-2),默认:1printBackground(boolean) - 是否打印背景,默认:trueignore(string|RegExp|Array) - 要忽略的错误(字符串、正则或数组),默认:[]debug(boolean) - 是否输出调试信息,默认:false
返回值:
{
success: boolean, // 是否成功
path?: string, // 输出文件路径(文件输出时)
stream?: Readable, // PDF Stream(流式输出时)
size: number, // 文件大小(字节)
title?: string, // 页面标题(URL 输入时)
error?: string, // 错误信息(失败时)
ignored?: boolean // 是否被忽略(错误被忽略时)
}setVerbose(verbose)
设置是否输出详细日志。
参数:
verbose(boolean) - 是否输出日志,默认:true
PAGE_SIZE_CONFIG
页面尺寸配置对象,包含所有可用的页面尺寸。
示例
示例 1:转换公开网页
# 命令行
webpage2pdf https://www.example.com -o example.pdf// 函数调用
const { generatePdf } = require('webpage2pdf');
await generatePdf('https://www.example.com', './example.pdf');示例 2:转换需要认证的页面
# 命令行
webpage2pdf https://api.example.com/page \
--header "Authorization:Bearer token" \
-o authenticated.pdf// 函数调用
const { generatePdf } = require('webpage2pdf');
await generatePdf('https://api.example.com/page', './authenticated.pdf', {
headers: {
'Authorization': 'Bearer token'
}
});示例 3:等待动态内容加载
# 命令行
webpage2pdf https://example.com/dynamic-page \
--selector "#content" \
-w 10000 \
-o dynamic-page.pdf// 函数调用
const { generatePdf } = require('webpage2pdf');
await generatePdf('https://example.com/dynamic-page', './dynamic-page.pdf', {
selector: '#content',
waitTime: 10000
});示例 4:流式输出
// 函数调用 - 返回 Stream
const { generatePdf } = require('webpage2pdf');
const fs = require('fs');
const result = await generatePdf('https://example.com', null);
if (result.success) {
result.stream.pipe(fs.createWriteStream('output.pdf'));
}示例 5:HTML 字符串输入
const { generatePdf } = require('webpage2pdf');
const html = `
<html>
<head><title>Test</title></head>
<body><h1>Hello World</h1></body>
</html>
`;
const result = await generatePdf(html, './output.pdf');示例 6:多页面合并
const { generatePdf } = require('webpage2pdf');
const urls = [
'https://example.com/page1',
'https://example.com/page2',
'https://example.com/page3'
];
// 合并多个页面到一个 PDF
const result = await generatePdf(urls, './combined.pdf', {
pageSize: 'A4',
waitTime: 5000
});注意:当前多页面合并使用简单的 Buffer 拼接方式,可能无法正确处理复杂的 PDF 结构。如果需要专业的 PDF 合并功能(如保留书签、目录等),建议:
- 使用
pdf-lib等专业库自行实现合并逻辑 - 先分别生成各个 PDF,然后使用专业工具合并
示例 7:错误忽略
const { generatePdf } = require('webpage2pdf');
const result = await generatePdf('https://example.com', './output.pdf', {
ignore: ['timeout', /网络错误/], // 忽略特定错误
debug: true // 开启调试模式
});示例 8:自定义边距和缩放
const { generatePdf } = require('webpage2pdf');
const result = await generatePdf('https://example.com', './output.pdf', {
margin: { top: '20mm', right: '15mm', bottom: '20mm', left: '15mm' },
scale: 0.9, // 缩放 90%
printBackground: true // 打印背景
});示例 9:批量转换
const { generatePdf, setVerbose } = require('webpage2pdf');
// 关闭详细日志
setVerbose(false);
const urls = [
'https://example.com/page1',
'https://example.com/page2',
'https://example.com/page3'
];
async function batchConvert() {
for (const url of urls) {
const result = await generatePdf(url, `./${Date.now()}.pdf`);
console.log(result.success ? '✓' : '✗', url);
}
}
batchConvert();支持的页面尺寸
A4: 210mm × 297mm(标准 A4)A4_PRINT: 216mm × 291mm(A4 打印尺寸)A3: 297mm × 420mm(A3)LETTER: 8.5in × 11in(美国 Letter)
技术说明
- 使用 Puppeteer 进行网页渲染和 PDF 生成
- 优先使用系统 Chrome(如果可用),否则使用 Puppeteer 内置的 Chromium
- 支持多种输入类型:URL、HTML 字符串、Buffer、Stream、URL 数组
- 支持流式输出:可以返回 Stream 而不只是文件
- 支持多页面合并:可以将多个网页合并成一个 PDF(当前使用简单拼接方式,适合简单场景)
- 支持自定义请求头(用于需要认证的页面)
- 支持等待特定元素加载(适用于动态内容)
- 支持错误忽略机制(可以忽略特定错误)
- 默认文件名:如果不指定
-o参数,会自动使用页面的document.title作为文件名 - 时间戳格式:文件名中的时间戳格式为
YYYYMMDDHHmm(如:202512251539)
新功能特性
✨ 流式输出
支持返回 Stream,适合管道操作和流式处理:
const result = await generatePdf('https://example.com', null);
result.stream.pipe(fs.createWriteStream('output.pdf'));✨ 多种输入类型
- URL:
'https://example.com' - HTML 字符串:
'<html>...</html>' - HTML Buffer:
Buffer.from('<html>...</html>') - HTML Stream:
fs.createReadStream('input.html') - URL 数组:
['url1', 'url2'](多页面合并,当前使用简单拼接方式)
✨ 错误忽略
可以忽略特定错误,避免因非致命错误中断流程:
await generatePdf(url, './output.pdf', {
ignore: ['timeout', /网络错误/]
});✨ 增强配置
- 自定义页边距:
margin: { top: '20mm', right: '15mm', ... } - 缩放比例:
scale: 0.9 - 背景打印控制:
printBackground: true/false - 调试模式:
debug: true
注意事项
- 首次运行:如果使用 Puppeteer 内置的 Chromium,首次运行时会自动下载(约 200MB)
- 网络连接:确保可以访问目标网页
- 页面加载:对于动态内容较多的页面,建议增加等待时间或使用
--selector选项 - 认证页面:如果需要访问需要认证的页面,使用
--header选项或headers参数添加认证信息 - 多页面合并:当前实现使用简单的 Buffer 拼接方式,适合简单场景。如果需要专业的 PDF 合并功能(保留书签、目录、元数据等),建议使用
pdf-lib等专业库自行实现合并逻辑 - 流式输出:使用流式输出时,确保及时处理 Stream,避免内存占用过大
- HTML 输入:使用 HTML 字符串输入时,确保 HTML 格式正确,否则可能渲染失败
故障排除
问题:找不到 Chrome
解决方案:
- macOS: 确保已安装 Google Chrome
- Linux: 安装 Chromium 或使用
puppeteer内置版本 - Windows: 确保 Chrome 已安装
问题:页面加载超时
解决方案:
- 增加等待时间:
-w 10000或waitTime: 10000 - 使用选择器等待:
--selector "#content"或selector: '#content'
问题:PDF 内容不完整
解决方案:
- 增加等待时间
- 使用
--selector或selector等待关键元素加载 - 检查网页是否有动态加载的内容
问题:多页面合并失败或合并后的 PDF 无法正常打开
解决方案:
- 确保所有 URL 都可以正常访问
- 检查网络连接
- 使用
ignore选项忽略非致命错误 - 重要:当前实现使用简单的 Buffer 拼接,可能无法正确处理复杂的 PDF 结构
- 如果需要专业的 PDF 合并功能,建议:
- 先分别生成各个 PDF 文件
- 使用
pdf-lib、pdf-merger-js等专业库进行合并 - 或使用命令行工具如
pdftk、ghostscript进行合并
专业合并示例:
const { PDFDocument } = require('pdf-lib');
const { generatePdf } = require('webpage2pdf');
const fs = require('fs');
// 1. 分别生成各个 PDF
const urls = ['url1', 'url2', 'url3'];
const pdfFiles = [];
for (const url of urls) {
const result = await generatePdf(url, `./temp-${Date.now()}.pdf`);
if (result.success) {
pdfFiles.push(result.path);
}
}
// 2. 使用 pdf-lib 正确合并
const mergedPdf = await PDFDocument.create();
for (const pdfPath of pdfFiles) {
const pdfBytes = fs.readFileSync(pdfPath);
const pdf = await PDFDocument.load(pdfBytes);
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
pages.forEach((page) => mergedPdf.addPage(page));
}
const mergedPdfBytes = await mergedPdf.save();
fs.writeFileSync('./merged.pdf', mergedPdfBytes);
// 3. 清理临时文件
pdfFiles.forEach(fs.unlinkSync);问题:流式输出不工作
解决方案:
- 确保
outputPath设置为null - 检查返回的
stream对象 - 确保及时处理 Stream,避免内存泄漏
相关方案对比
如果你需要了解其他页面转 PDF 的方案,可以参考以下对比:
主流方案对比
| 方案 | 渲染质量 | JS支持 | 资源占用 | 速度 | 维护状态 | 成本 | |------|---------|---------|---------|------|---------|------| | Puppeteer(本项目) | ⭐⭐⭐⭐⭐ | ✅ 完整 | 高 (~200MB) | 中等 | ✅ 活跃 | 免费 | | Playwright | ⭐⭐⭐⭐⭐ | ✅ 完整 | 高 (~300MB) | 中等 | ✅ 活跃 | 免费 | | wkhtmltopdf | ⭐⭐⭐ | ⚠️ 有限 | 低 (~50MB) | 快 | ❌ 停止 | 免费 | | html2pdf.js | ⭐⭐⭐ | ⚠️ 有限 | 低 | 快 | ✅ 活跃 | 免费 | | Gotenberg | ⭐⭐⭐⭐⭐ | ✅ 完整 | 高 | 中等 | ✅ 活跃 | 免费 | | Prince XML | ⭐⭐⭐⭐⭐ | ⚠️ 有限 | 中等 | 快 | ✅ 活跃 | 💰 商业 |
方案详情
1. Puppeteer(本项目使用)
- 技术栈:Node.js + Chrome DevTools Protocol
- 优点:现代 Web 支持完整、动态内容处理强、渲染质量高、功能丰富
- 缺点:资源占用大(~200MB)、启动较慢
- 适用场景:现代 Web 应用、需要等待动态内容、需要高质量 PDF
2. Playwright
- 技术栈:Node.js + 多浏览器引擎
- 优点:支持多浏览器引擎、自动等待机制更智能、API 设计更现代
- 缺点:资源占用更大、相对较新
- 适用场景:需要跨浏览器兼容性、自动化测试 + PDF 生成
3. wkhtmltopdf
- 技术栈:C++ + Qt WebKit
- 优点:轻量级(~50MB)、启动速度快、资源占用小
- 缺点:基于旧版 WebKit、不支持现代 JavaScript、CSS3 支持有限、已停止维护
- 适用场景:简单静态页面、批量处理、资源受限环境
4. html2pdf.js / jsPDF
- 技术栈:纯前端 JavaScript
- 优点:无需后端支持、客户端直接生成、轻量级
- 缺点:渲染质量一般(基于 Canvas)、不支持复杂 CSS、分页控制有限
- 适用场景:简单页面转换、无需后端支持、客户端生成
5. Gotenberg
- 技术栈:Docker + Chromium
- 优点:容器化部署、基于 Chromium 渲染质量高、RESTful API
- 缺点:需要 Docker 环境、需要服务器资源
- 适用场景:微服务架构、容器化部署、需要 API 接口
选择建议
- 现代 Web 应用(React/Vue/Angular):推荐 Puppeteer 或 Playwright
- 简单静态页面:推荐 wkhtmltopdf 或 html2pdf.js
- 批量处理:根据复杂度选择 wkhtmltopdf(简单)或 Puppeteer(复杂)
- 微服务架构:推荐 Gotenberg
- 前端直接生成:推荐 html2pdf.js 或 jsPDF
- 专业排版需求:推荐 Prince XML(商业)
本项目(webpage2pdf)的优势
基于 Puppeteer,特别适合:
- ✅ 现代 Web 应用(React/Vue/Angular)
- ✅ 需要等待动态内容加载
- ✅ 需要高质量 PDF 输出
- ✅ Node.js 环境
差异化特性:
- 同时支持命令行和函数调用
- 支持流式输出
- 支持多种输入类型(URL、HTML、Buffer、Stream)
- 支持多页面合并
- 易用的 API 设计
更多详细对比请参考 README 中的"相关方案对比"章节。
语言
许可证
MIT
贡献
欢迎提交 Issue 和 Pull Request!
