WeasyPrint 不执行 JavaScript,需在生成 HTML 阶段完成数据填充;支持 HTML(string=...)、filename=...、url=... 三种加载方式,但仅限 file://;CSS 分页需用 @page,中文字体须通过 @font-face 显式引入并配 base_url。

WeasyPrint 能直接把 HTML + CSS 渲染成 PDF,但「动态」不是它原生支持的——它不执行 JavaScript,也不连接后端。所谓“动态”,实际得靠你在生成 HTML 阶段就完成数据填充,再交给 WeasyPrint。
为什么 weasyprint.HTML 不能直接渲染含 JS 的页面
WeasyPrint 是纯 Python 实现的渲染引擎,它解析 HTML 和 CSS,但跳过所有 <script> 标签,也不启动浏览器环境。如果你的 HTML 依赖 fetch、Vue.mount 或 DOM 操作来显示内容,PDF 里只会看到空容器或初始骨架。
- 常见错误现象:
AttributeError: 'NoneType' object has no attribute 'text'(因 JS 未执行,元素为空) - 正确做法:用 Jinja2、
str.format()或 FastAPI/Flask 的模板引擎,在调用WeasyPrint前就把数据写进 HTML 字符串或文件 - 若必须走前端渲染流程(比如已有 React 页面),得换方案:用 Playwright 或 Puppeteer 截图 PDF,WeasyPrint 不适合
WeasyPrint 加载 HTML 的三种方式及选择建议
它提供 HTML(string=...)、HTML(filename=...)、HTML(url=...) 三种入口,但行为差异明显:
-
HTML(string=...):最常用,适合模板渲染后直接传字符串,注意编码要为 UTF-8,否则中文乱码;推荐加encoding='utf8'参数 -
HTML(filename=...):读本地文件,路径必须存在且可读;CSS 若用相对路径(如link href="style.css"),需确保 CSS 文件与 HTML 同目录,或用base_url显式指定根路径 -
HTML(url=...):仅支持file://协议,不支持http://;想加载远程 HTML?先用requests.get拿到内容,再走string=...
CSS 控制分页和打印样式的关键写法
WeasyPrint 支持大部分 CSS Paged Media 规范,但部分属性兼容性有限,容易导出失败或错位:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
立即学习“Python免费学习笔记(深入)”;
- 强制分页用
@page { size: A4; margin: 1cm; },不要只写size: a4(大小写敏感) - 避免在
<body>上设height: 100vh——PDF 没 viewport 概念,会撑破页面;改用min-height或块级容器控制 - 分页控制优先级:
page-break-inside: avoid对 flex/grid 容器无效,得套一层<div style="display: block"> - 中文字体必须显式声明:
@font-face { src: url('./simhei.ttf'); },且字体文件路径要通过base_url可达,否则显示方框
中文乱码、字体缺失和性能卡顿的实际解法
这三个问题几乎必现,根源都在字体处理链路上:
- 报错
Failed to load font "SimHei":不是名字写错,而是 WeasyPrint 默认只认系统字体(Linux 无 SimHei),必须用@font-face引入 ttf 文件,并确保base_url指向含该文件的目录 - 生成慢(>5 秒 / 页):关闭
presentational_hints=True(默认开启),禁用url_fetcher中不必要的重试逻辑,大表格用table-layout: fixed加速布局 - PDF 中文标点挤压:CSS 加
text-rendering: optimizeLegibility无效,改用font-feature-settings: "liga"控制连字,或直接换 Noto Sans CJK
真正麻烦的是字体嵌入——WeasyPrint 不自动打包字体,ttf 文件必须随代码部署,且路径在不同环境(开发机、Docker、服务器)下容易断链。建议把字体文件放在项目 static/fonts/ 下,统一用 base_url=os.path.abspath('static') 锚定。


















