真正能稳定落地的方案是纯html/css实现的「伪图表」:用table+bgcolor模拟柱状图,所有内容内联、无外链、100%可渲染;matplotlib/png/base64方案因邮件客户端屏蔽图片或不支持data uri而不可靠。

直接用 Python 生成带图表的 HTML 邮件发给运维同事,关键不是“能不能画图”,而是“邮件客户端能不能显示图”——绝大多数企业邮箱(包括 Outlook、腾讯企业邮、Gmail)会屏蔽外部图片、拒绝执行 JS,matplotlib 或 plotly 生成的 PNG/SVG 如果走外链或 base64 嵌入,极大概率被拦截或显示为空白。真正能稳定落地的方案,是纯 HTML/CSS 实现的「伪图表」:用表格+背景色+内联样式模拟柱状图/饼图,所有内容都在一个 HTML 字符串里,无依赖、无外链、100% 可渲染。
为什么不能用 matplotlib + savefig 发 PNG 再嵌入?
看似合理,但实际踩坑密集:
-
smtplib发送时若把 PNG 作为image/png附件嵌入,Gmail 和 Outlook 会把它当“可疑富媒体”,降权进推广栏甚至直接过滤 - 用
base64编码后塞进<img src="data:image/png;base64,...">,部分客户端(尤其是 Outlook 桌面版)根本不解析 data URI - 即使显示出来,图片尺寸难控制、无响应式、无法随数据动态缩放,比如磁盘使用率从 95% → 98%,你得手动调 height 值
- HTML 邮件对 CSS 支持极弱:
flex、grid、@media全部失效,只能靠<table> 和内联 <code>style用 table + bgcolor 实现可伸缩柱状图(含 0% 边界处理)
核心逻辑:把百分比转成
'''<td> 的 <code>height,再用两个嵌套<tr> 分别表示「已用」和「剩余」区域,避免 0% 时空白 td 被渲染成默认高度(这是原始 demo 最常出错的地方)。<p>示例函数:</p> <pre class="brush:php;toolbar:false;">def render_bar(percent, width=120, height=20, color="#4CAF50"): # 强制 0% → height="0%",100% → height="100%" used_height = f"{int(percent)}%" if 0 <tr> <td height="{used_height}" bgcolor="{color}" style="font-size:12px;color:white;text-align:center;line-height:{height}px;">{percent}%</td> </tr><tr> <td height="{empty_height}" bgcolor="#f1f1f1"></td> </tr></pre> </tr>调用:
render_bar(87)输出一个 120×20 像素、绿色占 87% 高度、右下角标 “87%” 的柱子,0% 时第二行占满、第一行 collapse 为 0 高度,不会留白。拼接完整 HTML 邮件时必须绕开的 MIME 陷阱
用
yagmail或smtplib发送前,HTML 字符串本身没问题,但容器结构错了就进垃圾箱:- 别用
MIMEText(html_str, "html")单层发送 —— Gmail 会认为“没正文”,直接标记低可信度 - 必须用
MIMEMultipart("alternative")包一层,并附上纯文本备选(哪怕只写一行"请使用 HTML 邮件客户端查看") - 如果同时带附件(如导出的 CSV),根容器得是
MIMEMultipart("mixed"),HTML 部分再嵌套在"alternative"子部件里,顺序不能颠倒 - 中文标题必须用
Header(subject, "utf-8").encode()编码,否则收件箱显示乱码或被拒收
真实巡检场景下的最小可行 HTML 结构
服务器磁盘巡检邮件不需要炫技,只要三块信息:主机名、路径、使用率柱图。下面这个片段可直接插入
中,兼容所有主流邮箱客户端:主机 挂载点 使用率 10.1.1.1 / {render_bar(72)} 10.1.1.1 /opt/data01 {render_bar(94, color="#f44336")} 注意:
render_bar返回的是完整 HTML 片段,直接用f-string插入即可;红色阈值(>90%)单独传参改色,不用额外判断逻辑。最易被忽略的一点:所有
<table> 必须带 <code>cellpadding="0" cellspacing="0",否则 Outlook 会自动加白边,柱图之间出现难看缝隙。这不是样式问题,是 Outlook 渲染引擎的硬规则。 - 别用
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











