You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在含Pandas数据源的Python Gmail邮件脚本中添加图片失败的求助

在含Pandas数据源的Python Gmail邮件脚本中添加图片失败的求助

我明白你现在的困扰——不带图片时邮件能正常发送,但想把Pandas生成的柱状图内嵌到邮件里就失效了。这是因为你当前只用了MIMEText来构建邮件内容,而内嵌图片需要支持多部分内容的MIMEMultipart容器,还要把图片作为内嵌附件(而非普通附件),再通过CID(内容ID)在HTML里引用它。下面是具体的修复方案:

核心修改要点

  • 改用MIMEMultipart作为邮件的根容器,它能同时承载HTML文本和内嵌资源
  • 将本地图片读取为MIMEImage对象,设置唯一的Content-ID,并添加到邮件容器中
  • 把HTML里的本地图片路径替换为cid:你的图片CID,这样邮件客户端才能找到内嵌的图片
  • 调整邮件内容序列化的时机,要在所有部分都添加完成后再调用as_string()

修改后的完整代码

import cx_Oracle
import pandas as pd
import sqlalchemy
import smtplib
import matplotlib

from email.mime.text import MIMEText
from email.header import Header
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart  # 新增:导入多部分邮件容器

image_path="C:\\Users\\n.restaino\\PycharmProjects\\pythonProject\\.venv\\barchart1.png"

def send_simple_email(host, subject, from_addr, to_addr, body, password, image_path=None):
    # 重构:改用MIMEMultipart作为邮件根容器
    message = MIMEMultipart()
    message['From'], message['To'] = from_addr, to_addr
    message['Subject'] = Header(subject, "utf-8")

    # 添加HTML文本内容
    html_part = MIMEText(body, 'html', 'utf-8')
    message.attach(html_part)

    # 若传入图片路径,将其作为内嵌资源添加到邮件
    if image_path:
        try:
            with open(image_path, 'rb') as img_file:
                img = MIMEImage(img_file.read())
                # 设置唯一的Content-ID,HTML将通过此ID引用图片
                img.add_header('Content-ID', '<bar_chart_cid>')
                # 标记为内嵌附件(而非可下载的普通附件)
                img.add_header('Content-Disposition', 'inline', filename='barchart1.png')
                message.attach(img)
        except Exception as e:
            print(f"读取或添加图片失败: {e}")

    try:
        import ssl
        context = ssl.create_default_context()
        server = smtplib.SMTP_SSL(host, 465, context=context)
        server.login(from_addr, password)
        # 所有内容添加完成后再序列化邮件
        server.sendmail(from_addr, [to_addr], message.as_string())
    except Exception as e:
        print(f"Error: {e}")
    finally:
        server.quit()
        print("Email Sent!")

if __name__ == "__main__":
    # Connection details
    user = 'USER'
    pw = 'PWD'
    host = '10.0.0.1'
    port = '1521'
    db = 'DB'

    engine = sqlalchemy.create_engine('oracle+cx_oracle://' + user + ':' + pw + '@' + host + ':' + port + '/?service_name=' + db)
    my_query='SELECT * FROM T_C10'
    df = pd.read_sql(my_query, engine)
    ax = df.plot.bar(x='tipo', y='numero', rot=0)

    fig = ax.get_figure()
    fig.savefig(image_path)
    # 关闭matplotlib画布释放内存
    matplotlib.pyplot.close(fig)

    host = "smtp.gmail.com"  # Using Gmail server to send the email
    subject = "[AUTO EMAIL] Report Sales"
    # 修改HTML图片引用:用CID替换本地无效路径
    html_body = f"""<h1> Sales Report </h1> 
                    {df.to_html()}
                    <img src="cid:bar_chart_cid" alt="销售数据柱状图">
    """

    from_addr, to_addr = "C.D@gmail.com", "A.B@gmail.com"

    # Get Passwork from user
    import getpass

    password = '1234 5678 '  # 实际使用建议用getpass.getpass()避免硬编码密码

    # 发送邮件时传入图片路径参数
    send_simple_email(host, subject, from_addr, to_addr, html_body, password, image_path)

关键修改说明

  1. 为什么用MIMEMultipart?
    MIMEText只能承载单一的文本/HTML内容,而MIMEMultipart可以把多个不同类型的内容(HTML、图片、附件等)组合成一个完整的邮件,是处理复杂邮件的标准方式。

  2. 为什么要设置Content-ID?
    邮件里的本地路径对收件人完全无效(他们的设备上没有这个文件),所以需要给内嵌图片分配一个唯一的CID,HTML通过cid:xxx的方式引用这个ID,邮件客户端就能找到对应的内嵌图片资源。

  3. 为什么要关闭matplotlib的fig?
    生成图片后关闭画布可以释放内存资源,避免长时间运行导致的内存泄漏问题。


备注:内容来源于stack exchange,提问作者Nixi80

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 15:14:31