Python发送同时包含纯文本与HTML正文的邮件实现及问题解决
问题根因
你遇到的正文显示异常、HTML被判定为附件的问题,本质都是MIME多部分结构的语义标识和部件顺序不符合邮件传输规范:
- 给
MIMEMultipart('')传空字符串会导致容器的Content-Type头部失效,邮件客户端无法识别容器的用途,会把后续附加的内容默认归类为附件。 - 只要容器类型不对,哪怕同时添加了纯文本和HTML版本,客户端也不会把二者判定为「同一份正文的不同兼容选项」,只会优先展示第一个能解析的部分,剩下的全部按附件接收。
正确结构实现
根据邮件是否需要附加真实文件,分两种标准结构:
- 无额外附件、仅需要双格式正文:直接使用
alternative类型的多部分容器,按照纯文本在前、HTML在后的顺序附加内容。这个容器的官方语义就是“内部所有部件都是等价的正文替代版本”,客户端会自动选择自身支持的最高规格版本展示,全程不会把这些内容判定为附件。 - 带真实附件(文档、图片等独立文件):最外层使用
mixed类型的多部分容器,先在内部嵌套一个alternative子容器承载双格式正文,再把真实附件附加到最外层的mixed容器即可。
修正后的可直接运行代码
场景1:仅双格式正文,无额外附件
import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText me = "my@email.com" you = "your@email.com" # 直接指定alternative容器类型,禁止传空字符串 msg = MIMEMultipart('alternative') msg['Subject'] = "Link" msg['From'] = me msg['To'] = you text = '这是纯文本正文' html = """\ <html> <head></head> <body> <p>您好!<br> 您近来可好?<br> 这是您想要的链接。 </p> </body> </html> """ # 顺序严格遵循:纯文本在前,HTML在后 part1 = MIMEText(text, 'plain') part2 = MIMEText(html, 'html') msg.attach(part1) msg.attach(part2) # SMTP发送流程与原有逻辑一致 mail = smtplib.SMTP('smtp.live.com', 587) mail.ehlo() mail.starttls() mail.login('userName', 'password') mail.sendmail(me, you, msg.as_string()) mail.quit()
场景2:双格式正文 + 独立真实附件
import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.application import MIMEApplication me = "my@email.com" you = "your@email.com" # 最外层用mixed容器,统一承载正文和所有附件 msg = MIMEMultipart('mixed') msg['Subject'] = "带附件的双格式正文邮件" msg['From'] = me msg['To'] = you text = '这是纯文本正文' html = """\ <html> <head></head> <body> <p>您好!<br> 您近来可好? </p> </body> </html> """ # 嵌套alternative子容器,专门承载双格式正文,避免被判定为附件 msg_body = MIMEMultipart('alternative') msg_body.attach(MIMEText(text, 'plain')) msg_body.attach(MIMEText(html, 'html')) # 将正文容器挂载到最外层mixed容器下 msg.attach(msg_body) # 附加独立文件,以PDF附件为例 with open("test.pdf", "rb") as f: attach_part = MIMEApplication(f.read(), _subtype="pdf") # 显式标记为附件,和正文做区分 attach_part.add_header('Content-Disposition', 'attachment', filename="test.pdf") msg.attach(attach_part) # 后续SMTP发送逻辑与原有代码一致 mail = smtplib.SMTP('smtp.live.com', 587) mail.ehlo() mail.starttls() mail.login('userName', 'password') mail.sendmail(me, you, msg.as_string()) mail.quit()
常见踩坑点
alternative容器内的部件顺序绝对不能调换,如果把HTML放在纯文本前面,部分低版本邮件客户端会优先级判断错误,把HTML部分判定为附件。- 不要把独立附件直接放在
alternative容器里,所有真实附件必须挂载在最外层的mixed容器下,否则会被当成正文的一个版本,无法正常下载。 - 检查HTML部件是否被额外添加了
Content-Disposition: attachment头部,只要存在这个标识,不管放在哪个容器里,都会被判定为附件,纯文本和HTML正文的MIMEText默认不需要加这个头部。
内容的提问来源于stack exchange,提问作者Raylene
相关产品推荐
相关产品推荐

