You need to enable JavaScript to run this app. Python发送同时包含纯文本与HTML正文的邮件实现及问题解决
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.27 18:36:03