BooAi-web-to-pdf — 网页转PDF引擎
Boo哥AI智写 · 专业AI标书写作引擎 | 在线页面 → PDF 转换工具
概述
将在线幻灯片/原型页面自动转为 PDF。核心思路:Playwright 无头浏览器逐页截图 + img2pdf 无损合成。
适用场景
- 在线 Axure 原型导出(axureshow.com 等平台)
- HTML 幻灯片/演示文稿(圆点导航、缩略图导航)
- 单页 Web 应用逐页导出
- 任何可通过点击导航逐页切换的网页
不适用
- 需要登录认证的页面
- 无限滚动加载的页面
- 纯静态无导航的单页
工作流程
第一步:探索页面结构
用 Playwright 访问 URL,分析导航方式:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True, args=["--no-sandbox", "--ignore-certificate-errors"])
page = browser.new_page(viewport={"width": 1920, "height": 1080})
page.goto(url, wait_until="commit", timeout=30000)
page.wait_for_timeout(5000)
# 检测导航类型
dots = page.evaluate("() => document.querySelectorAll('#nav .dot, .dot, [class*=pagination] button').length")
text = page.evaluate("() => document.body.innerText.substring(0, 500)")
print(f"圆点导航数: {dots}")
print(f"页面文本片段: {text}")
print(f"页面标题: {page.title()}")
browser.close()
常见导航模式识别:
| 模式 | 选择器示例 | 切换方式 |
|------|-----------|---------|
| 圆点按钮 | #nav .dot[data-i] | .click() |
| 侧边栏树 | .sitemapPage, [data-pageid] | .click() 后等 iframe 加载 |
| 左右箭头 | .arrow-left, .arrow-right, [class*=arrow] | .click() |
| 键盘翻页 | 无可见导航 | page.keyboard.press("ArrowRight") |
第二步:编写转换脚本
核心模板 — 根据第一步识别的导航模式选择:
from playwright.sync_api import sync_playwright
from PIL import Image
import img2pdf, os
URL = "<目标URL>"
OUTPUT = "<输出PDF路径>"
TEMP = "temp_slides"
os.makedirs(TEMP, exist_ok=True)
with sync_playwright() as p:
browser = p.chromium.launch(headless=True, args=["--no-sandbox", "--ignore-certificate-errors"])
context = browser.new_context(viewport={"width": 1920, "height": 1080}, device_scale_factor=2)
# ★ 关键:劫持字体系统防止 screenshot 超时
context.add_init_script("""
const _FontFace = window.FontFace;
window.FontFace = function(family, source, descriptors) {
const ff = new _FontFace(family, source, descriptors);
Object.defineProperty(ff, 'status', { get: () => 'loaded' });
ff.load = () => Promise.resolve(ff);
return ff;
};
Object.defineProperty(document, 'fonts', {
get: () => ({ ready: Promise.resolve(), add: () => {}, clear: () => {} }),
configurable: true,
});
""")
page = context.new_page()
# 阻断外部字体下载
page.route("**/*", lambda route: (
route.abort() if any(x in route.request.url.lower() for x in
['.woff', '.ttf', '.otf', '.eot', 'fonts.googleapis.com', 'fonts.gstatic.com'])
else route.continue_()
))
page.goto(URL, wait_until="commit", timeout=30000)
page.wait_for_timeout(5000)
# 隐藏导航UI元素
page.evaluate("""() => {
const nav = document.getElementById('nav');
if (nav) nav.style.display = 'none';
document.querySelectorAll('.snap-close, .ai-highlight, .chrome, [class*=toolbar]').forEach(el => el.style.display = 'none');
}""")
# 获取页数(此处以圆点导航为例,其他模式需调整)
total = page.evaluate("() => document.querySelectorAll('#nav .dot').length")
print(f"共 {total} 页")
screenshots = []
for i in range(total):
page.evaluate(f"document.querySelectorAll('#nav .dot')[{i}].click()")
page.wait_for_timeout(1200)
path = os.path.join(TEMP, f"slide_{i:03d}.png")
page.screenshot(path=path, full_page=False, timeout=60000)
screenshots.append(path)
print(f" [{i+1}/{total}]")
browser.close()
# 合成 PDF(用 img2pdf,不用 Pillow — Pillow 可能缺 JPEG 编码器)
with open(OUTPUT, 'wb') as f:
f.write(img2pdf.convert(screenshots))
# 清理
for p in screenshots:
os.remove(p)
os.rmdir(TEMP)
print(f"完成: {OUTPUT}")
第三步:验证与交付
- 打开生成的 PDF,确认页数正确
- 检查第一页和最后一页内容是否完整
- 若文件过大(>30MB),可降
device_scale_factor为 1
关键陷阱
陷阱 1:screenshot 字体加载超时
症状:Page.screenshot: Timeout 30000ms exceeded. waiting for fonts to load...
根因:Playwright 内部在截图前等待 document.fonts.ready,国内访问 Google Fonts 等 CDN 超时导致永远无法 ready。
修复:必须在 context.add_init_script 中劫持 FontFace 构造函数和 document.fonts 对象(见上面模板),仅靠 page.route 阻断网络请求不够,因为字体可能在路由注册前已开始加载。
陷阱 2:Pillow PDF 合成报 KeyError: 'JPEG'
症状:KeyError: 'JPEG' 在 PdfImagePlugin.py 中
根因:Windows 上通过 pip 安装的 Pillow wheel 可能未编译 JPEG 编码器。
修复:使用 img2pdf 库替代 Pillow 的 Image.save() 进行 PDF 合成。img2pdf 是纯 Python 实现,不依赖外部编解码器。
pip install img2pdf playwright
playwright install chromium
陷阱 3:页面加载超时
症状:Page.goto: Timeout 30000ms exceeded
修复:
- 用
wait_until="commit"代替"networkidle"(后者要求 500ms 内无网络请求) - 手动
wait_for_timeout(5000)等待 JS 渲染完成 - 若仍超时,检查是否需要代理或网络连通性
依赖
| 依赖 | 用途 | 安装 |
|------|------|------|
| Python 3.9+ | 运行环境 | 系统 |
| playwright | 无头浏览器截图 | pip install playwright |
| chromium (playwright) | 浏览器内核 | playwright install chromium |
| img2pdf | 无损 PDF 合成 | pip install img2pdf |
脚本位置
可复用转换脚本位于:scripts/web_to_pdf.py
python scripts/web_to_pdf.py <URL> [--output out.pdf] [--scale 1|2]
版本
[v1.0.0] - 2026-05-26 · 初始版本 · CHANGELOG
<p align="center"> <strong>Powered by Boo哥AI智写</strong><br> 📧 联系与反馈:<a href="mailto:409966830@qq.com">409966830@qq.com</a><br> <sub>智写万象,标定未来</sub> </p>
Scan to join WeChat group