Python中使用EmailMessage和smtplib无法同时发送文本与HTML邮件
问题
使用EmailMessage和smtplib发送邮件时,单独发送纯文本邮件正常,但添加带嵌入图片的HTML内容(通过msg.add_alternative(html))后,打开邮件仅能看到HTML部分,纯文本内容仅在收件箱列表可见,打开邮件后不展示。
原代码片段:
import smtplib from email.message import EmailMessage from email.header import Header from email.utils import formataddr, make_msgid SENDER_MAIL = "sender@email.com" SENDER_PASSWORD = "sender password" SENDER_TITLE = "sender title" recipient = "recipient@email.com" subject = "This is the subject." body = "This is the body." html_string = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE-edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>email</title> </head> <body> <table> <tbody> <tr> <td> <td> <img width="138" height="138" src="cid:{dp_cid}" alt=""> </td> </td> </tr> <tbody> <table> <body> """ def compose_mail(subject, body, html): msg = EmailMessage() msg['Subject'] = subject msg['From'] = formataddr((str(Header(SENDER_TITLE, 'utf-8')), SENDER_MAIL)) msg['To'] = recipient # ================= Attach plain text as body msg.set_content(body, subtype="plain", charset='utf-8') # ================= Load images to the html dp_cid = make_msgid() msg.add_alternative(html.format(dp_cid=dp_cid[1:-1]), subtype='html') with open("Images/dp.png", "rb") as img: msg.get_payload()[1].add_related(img.read(), "image", "png", cid=dp_cid) # ================= Send mail server = smtplib.SMTP_SSL('server', port) server.login(SENDER_MAIL, SENDER_PASSWORD) server.send_message(msg) server.quit() print("Mail sent.") compose_mail(subject, body, html_string)
原因分析
问题核心在于直接通过msg.get_payload()[1]操作HTML部分添加关联资源,这种方式会破坏邮件的MIME结构,导致邮件客户端无法正确识别纯文本与HTML的替代关系,最终只渲染HTML内容。此外,原HTML代码存在标签错误(未闭合的<tbody>、<table>,多余嵌套的<td>),虽不直接影响纯文本显示,但会干扰HTML渲染效果。
解决方法
不要直接操作get_payload(),而是保存msg.add_alternative()返回的HTML部分对象,通过该对象添加嵌入资源;同时修正HTML标签错误。
修正后的代码:
import smtplib from email.message import EmailMessage from email.header import Header from email.utils import formataddr, make_msgid SENDER_MAIL = "sender@email.com" SENDER_PASSWORD = "sender password" SENDER_TITLE = "sender title" recipient = "recipient@email.com" subject = "This is the subject." body = "This is the body." html_string = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>email</title> </head> <body> <table> <tbody> <tr> <td> <img width="138" height="138" src="cid:{dp_cid}" alt=""> </td> </tr> </tbody> </table> </body> </html> """ def compose_mail(subject, body, html): msg = EmailMessage() msg['Subject'] = subject msg['From'] = formataddr((str(Header(SENDER_TITLE, 'utf-8')), SENDER_MAIL)) msg['To'] = recipient # 设置纯文本内容 msg.set_content(body, subtype="plain", charset='utf-8') # 添加HTML替代内容并获取对应对象 dp_cid = make_msgid() html_part = msg.add_alternative(html.format(dp_cid=dp_cid[1:-1]), subtype='html') # 向HTML部分添加嵌入图片 with open("Images/dp.png", "rb") as img: html_part.add_related(img.read(), "image", "png", cid=dp_cid) # 发送邮件 server = smtplib.SMTP_SSL('server', port) server.login(SENDER_MAIL, SENDER_PASSWORD) server.send_message(msg) server.quit() print("Mail sent.") compose_mail(subject, body, html_string)
关键修改点
- 保存
msg.add_alternative()返回的html_part对象,通过它添加关联资源,避免破坏MIME结构。 - 修正HTML标签错误:闭合
</tbody>、</table>、</html>,移除多余嵌套的<td>,修正IE-edge为IE=edge。
内容的提问来源于stack exchange,提问作者Milind Khobragade
相关产品推荐
相关产品推荐

