
1. 为什么选择WeasyPrint处理HTML转PDF最近在做一个自动化报表系统时我对比了市面上主流的Python PDF生成方案。PyPDF2处理现有PDF很擅长但生成能力弱ReportLab需要手动布局太麻烦而WeasyPrint就像发现的新大陆——它直接用HTMLCSS写文档布局这简直是为Web开发者量身定做的方案。WeasyPrint的核心优势在于完美支持Web标准。上周我接到个紧急需求要把一个包含复杂CSS动画的网页转成PDF。原本以为要重写整套样式结果WeasyPrint直接吃透了所有flex布局和transform属性连keyframes动画都能保留关键帧状态。不过要注意它不支持JavaScript渲染动态内容需要先用selenium等工具预处理。实际测试中转换一个20页的图文混排文档WeasyPrint比wkhtmltopdf快3倍左右内存占用只有后者的一半。有次批量生成100份合同普通云服务器上跑完仅用了2分钟。更惊喜的是它对中文排版的处理——自动换行、标点挤压这些细节都考虑到了不像某些库会把中文排成乱码。2. 跨平台安装避坑指南2.1 Windows系统特别准备很多人在Windows安装时卡在GTK3依赖这步。我建议直接使用MSYS2的UCRT64终端不是默认的MSYS2终端这样能避免后续各种路径问题。安装完GTK3后一定要检查bin目录是否包含以下关键dll文件libgtk-3-0.dlllibpango-1.0-0.dlllibcairo-2.dll如果遇到找不到GDK-PixBuf的错误试试这个命令pacman -S mingw-w64-ucrt-x86_64-gdk-pixbuf22.2 MacOS的简便安装通过Homebrew可以一键搞定brew install cairo pango gdk-pixbuf libffi pip install weasyprint最近帮同事处理过M1芯片的兼容问题发现需要额外设置环境变量export PKG_CONFIG_PATH/opt/homebrew/opt/libffi/lib/pkgconfig2.3 Linux的依赖管理Ubuntu/Debian系推荐用apt安装前置依赖sudo apt-get install build-essential python3-dev python3-pip python3-cffi python3-brotli libpango-1.0-0 libharfbuzz-dev libffi-devCentOS用户要注意默认源里的pango版本可能太低建议用EPEL源sudo yum install epel-release sudo yum install pango-devel libffi-devel3. 实战中的高效使用技巧3.1 样式优化最佳实践处理商务合同时发现用REM单位比PX更利于多尺寸适配。这是我的常用基准样式media print { :root { font-size: 6pt; } body { margin: 2rem; line-height: 1.6; -webkit-print-color-adjust: exact !important; } }表格边框打印经常消失的问题可以用这个hack解决table { border-collapse: collapse; border-style: hidden; } table td, table th { border: 1px solid black; }3.2 批量处理性能优化当需要处理上百个HTML文件时直接循环调用会非常慢。我封装了个多进程版本来压榨多核CPUfrom multiprocessing import Pool def batch_convert(args): html_path, pdf_path args HTML(html_path).write_pdf(pdf_path) with Pool(processes4) as pool: pool.map(batch_convert, [(finput_{i}.html, foutput_{i}.pdf) for i in range(100)])内存泄漏是个常见坑点。长时间运行的服务中建议定期重启进程或者用这个上下文管理器from contextlib import contextmanager contextmanager def weasyprint_context(): try: yield finally: import weasyprint weasyprint.document.document._url_fetcher.clear_cache()4. 企业级应用案例解析4.1 动态合同生成系统去年给某金融机构做的方案中我们结合Jinja2模板实现条款动态组装。关键点在于处理签名位置固定div styleposition: fixed; bottom: 3cm; right: 2cm; img src{{signature}} stylewidth: 4cm; /div遇到个有趣的问题客户要求每页合同底部都有水印但水印不能遮挡正文。最终方案是用伪元素实现page { bottom { content: url(watermark.png); opacity: 0.2; margin-bottom: 1cm; } }4.2 大数据量报表导出处理万行级数据表格时直接渲染会导致内存暴涨。我们的解决方案是分块处理chunk_size 500 for i in range(0, len(data), chunk_size): chunk_html render_template(table_chunk.html, rowsdata[i:ichunk_size]) HTML(stringchunk_html).write_pdf(target, appendbool(i))还有个报表常见的页眉页脚需求用page规则就能搞定page { size: A4; margin: 2cm; top-left { content: 机密文件; font-size: 9pt; } bottom-right { content: counter(page); } }最近遇到个客户需要将PDF上传到S3我们直接用boto3做了个自动管道import boto3 from io import BytesIO buffer BytesIO() HTML(stringhtml).write_pdf(buffer) s3 boto3.client(s3) s3.put_object(Bucketmy-bucket, Keyreport.pdf, Bodybuffer.getvalue())