推荐「Python→HTML(内联CSS/ base64图表)→PDF」链路,用pdfkit(依赖系统级wkhtmltopdf)生成中文报表,smtplib发信时需Header编码附件名,Linux用绝对路径crontab定时,排查聚焦字体、渲染缓存与SMTP策略。

用 matplotlib + pdfkit 生成带图表的 PDF 报表
纯 matplotlib 的 savefig() 只能存单图,日报需要整合标题、表格、多子图、文字说明——得先转 HTML 再转 PDF。推荐走「Python → HTML(含内联 CSS/图表 base64)→ PDF」链路,避免依赖系统字体或 LaTeX 环境。
关键点:别用 weasyprint(对中文支持弱、不渲染 JS 生成的图表),pdfkit(底层是 wkhtmltopdf)更稳。安装时注意:wkhtmltopdf 必须单独装系统级二进制,不能只 pip pdfkit。
实操建议:
- 用
pd.DataFrame.to_html(escape=False, index=False)渲染表格,提前把matplotlib图转为 base64 嵌入 - HTML 模板里写死
@font-face引入思源黑体等开源中文字体,并设body { font-family: "Source Han Sans SC"; } -
pdfkit.from_file()调用前加configuration = pdfkit.configuration(wkhtmltopdf="/usr/local/bin/wkhtmltopdf"),Linux/macOS 路径别写错
用 smtplib + email 发送带附件的日报邮件
别用第三方 SDK(如 yagmail),它会自动解码附件名,中文文件名在 Outlook 里常变乱码;原生 smtplib 控制力更强。
常见错误现象:UnicodeEncodeError: 'ascii' codec can't encode characters —— 根本原因是邮件头没声明编码,附件名没用 Header 包装。
实操建议:
- 收件人列表用
["a@x.com", "b@y.com"],别拼成字符串,否则会被当成一个地址 - 附件名必须用
from email.header import Header; Header("20240520_日报.pdf", "utf-8").encode()编码 - 发信服务器端口选 587(STARTTLS),别硬上 465(SSL),很多企业邮箱已弃用 465
- 密码字段填应用专用密码(如 Gmail 开启两步验证后生成的 16 位密钥),不是登录密码
用 schedule 或系统 crontab 触发每日定时执行
Python 的 schedule 库适合开发调试,但生产环境别依赖它长期运行——进程挂了就停更,且不支持跨机房高可用。真要稳,直接上系统级调度。
Linux 下用 crontab -e 写:
0 9 * * * /usr/bin/python3 /home/user/report/daily_report.py >> /home/user/report/cron.log 2>&1
注意三点:
- 绝对路径写全:
/usr/bin/python3不是python3,/home/user/report/daily_report.py不能用~/ - 脚本开头加
#!/usr/bin/env python3并chmod +x,cron 才认 shebang - 所有依赖路径(如字体文件、配置文件)在脚本里也必须用绝对路径,cron 环境变量极简,
$HOME都可能为空
处理中文乱码、图表缺失、邮件被拒这三类高频失败
90% 的失败集中在字体、渲染上下文、SMTP 策略三块,不是代码逻辑问题。
排查顺序和对策:
- PDF 里中文显示为方框?检查
wkhtmltopdf是否支持指定字体:运行wkhtmltopdf --version,若输出含Qt WebEngine(新版),需额外传参--enable-local-file-access --allow /tmp;旧版 QtWebKit 则需用--font-dir /path/to/fonts - PDF 中图表空白?确认
matplotlib后端不是Agg(它无 GUI,但生成 base64 没问题),真正问题是图表plt.show()后没plt.close(),缓存占满导致后续图渲染失败 - 邮件进垃圾箱或被拒?发信域名没配 SPF/DKIM 记录;临时解决:收件人先加白名单,正文加一句「本邮件由系统自动生成,请勿直接回复」,降低反垃圾引擎评分
最易被忽略的是:PDF 生成和邮件发送之间没加异常隔离。报表生成失败时,脚本应退出并写日志,而不是继续发空附件——这会导致运维误以为日报正常,实际数据断更三天才发现。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











