在含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)
关键修改说明
为什么用
MIMEMultipart?MIMEText只能承载单一的文本/HTML内容,而MIMEMultipart可以把多个不同类型的内容(HTML、图片、附件等)组合成一个完整的邮件,是处理复杂邮件的标准方式。为什么要设置Content-ID?
邮件里的本地路径对收件人完全无效(他们的设备上没有这个文件),所以需要给内嵌图片分配一个唯一的CID,HTML通过cid:xxx的方式引用这个ID,邮件客户端就能找到对应的内嵌图片资源。为什么要关闭matplotlib的fig?
生成图片后关闭画布可以释放内存资源,避免长时间运行导致的内存泄漏问题。
备注:内容来源于stack exchange,提问作者Nixi80
相关产品推荐
相关产品推荐

