Python如何拼接两个带表头DataFrame写入CSV并生成美观邮件正文表格
可直接适配的实现方案
不需要修改你现有send_mail、smtplib、email系列模块、configparser相关的已有逻辑,分两部分实现需求:
1. 生成指定格式的CSV附件
不要直接拼接两个DataFrame写入,通过内存流按要求插入分段标识,无需生成本地临时文件,直接适配MIMEApplication附件逻辑:
import pandas as pd from io import StringIO, BytesIO # 初始化内存文本流写CSV内容 csv_buf = StringIO() # 写入df1分段 csv_buf.write("The Errors in df1 are:\n") df1.to_csv(csv_buf, index=False, header=True, encoding="utf-8") csv_buf.write("\n") # 空行分隔两段,避免内容粘连 # 写入df2分段 csv_buf.write("The Errors in df2 are:\n") df2.to_csv(csv_buf, index=False, header=True, encoding="utf-8") # 转成带BOM的utf-8字节流,适配Excel直接打开不乱码 csv_bytes = BytesIO(csv_buf.getvalue().encode("utf-8-sig")) # 把csv_bytes传入你现有send_mail的附件参数即可,原有附件处理逻辑完全不用改
生成的CSV完全符合格式要求:先输出df1的标识行,再带表头输出df1全量数据,空行分隔后输出df2标识行和带表头的df2全量数据。
2. 生成高可读性邮件正文表格
之前用tabulate生成纯文本表格排版混乱,是因为不同邮件客户端的等宽字体渲染规则不一致,很容易出现列错位、文字拥挤的问题。既然你已经支持HTML格式的MIMEText正文,直接用pandas原生的to_html方法加内联CSS样式即可,无额外第三方依赖,所有主流邮件客户端(Outlook、网页邮箱、移动端邮箱)都能正常渲染,不会出现排版错乱:
# 表格基础样式:加内边距、边框、隔行变色,彻底解决拥挤问题 html_style = """ <style> .err-section { font-size: 15px; font-weight: bold; margin: 16px 0 8px; font-family: "Microsoft Yahei", sans-serif; } .err-table { border-collapse: collapse; width: 100%; font-family: "Microsoft Yahei", sans-serif; font-size: 14px; } .err-table th { background-color: #f0f0f0; border: 1px solid #ccc; padding: 8px 12px; text-align: left; } .err-table td { border: 1px solid #ccc; padding: 8px 12px; } </style> """ # 正文结构模板 html_body = """ <html> <body> <p style="font-family: 'Microsoft Yahei', sans-serif; font-size: 14px;"> 本次任务运行检测到如下错误数据,请及时处理: </p> <div class="err-section">The Errors in df1 are:</div> {df1_tbl} <div class="err-section">The Errors in df2 are:</div> {df2_tbl} </body> </html> """ # 把两个DataFrame转成带指定样式类的HTML表格,去掉pandas默认冗余边框属性,加隔行变色逻辑 df1_tbl_html = df1.to_html(index=False, classes="err-table", border=0) # 给表格插入隔行变色样式 df1_tbl_html = df1_tbl_html.replace("<tr>", '<tr style="background-color: #fafafa;">', 1) for i in range(3, len(df1)+2, 2): df1_tbl_html = df1_tbl_html.replace("<tr>", '<tr style="background-color: #fafafa;">', i) df2_tbl_html = df2.to_html(index=False, classes="err-table", border=0) df2_tbl_html = df2_tbl_html.replace("<tr>", '<tr style="background-color: #fafafa;">', 1) for i in range(3, len(df2)+2, 2): df2_tbl_html = df2_tbl_html.replace("<tr>", '<tr style="background-color: #fafafa;">', i) # 拼接最终HTML正文 final_mail_html = html_style + html_body.format(df1_tbl=df1_tbl_html, df2_tbl=df2_tbl_html) # 把final_mail_html传入你现有send_mail的正文参数,构造MIMEText时指定subtype='html'即可,原有逻辑无需修改
该方案的排版优势:
- 单元格设置了左右12px、上下8px的内边距,不会出现文字紧贴边框的拥挤问题
- 加了浅灰色边框和隔行变色,行与行区分度高,长表格阅读不容易串行
- 宽度自适应邮件窗口,不会出现横向滚动条或者列宽被压缩的问题
- 样式全部内联在HTML代码中,不会被邮件客户端的安全策略过滤
调用现有
send_mail函数时,直接传入final_mail_html作为HTML正文、csv_bytes作为CSV附件即可,不需要调整你已经编写完成的smtplib连接、configparser配置读取、MIME构造相关的任何代码。
内容的提问来源于stack exchange,提问作者unicorn
相关产品推荐
相关产品推荐

