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

使用smtplib发带图邮件遇550错误:多To头且仅首个收件人收信

问题分析

错误smtplib.SMTPDataError: (550, b'5.7.1 This message is not RFC 5322 compliant. There are multiple To headers')的核心原因是:MIMEMultipart对象的message["To"]赋值操作不会覆盖原有To头,而是追加新的To头。循环多次赋值后,邮件里会累积多个To字段,违反了邮件协议标准。

你尝试的两种写法都有问题:

  • 第一种每次循环都重新创建SSL连接并登录,效率极低,还会让原message对象不断累积To头;
  • 第二种把message["To"] = email放在循环外,变量email未定义,且循环内没更新To头,导致所有邮件都发给同一个人(直接报错)。
解决方案

核心思路:用原始邮件模板生成副本,每次给副本单独设置To头;同时只建立一次SMTP连接,循环发送所有邮件,既解决多To头问题又提升效率。

修改后的完整代码:

import smtplib, ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage

port = 465  # For SSL
app_pass = "my_app_password"
sender_email = "Sender@gmail.com"
receiver_emails = ["example1@gmail.com", "example2@gmail.com"]

# 构建不带To头的原始邮件模板
message_template = MIMEMultipart("alternative")
message_template["Subject"] = "Email Subject Line"
message_template["From"] = sender_email

# 邮件内容
text = """\
"""
html = """\
<html>
<body>
    <img src="cid:image1">
</body>
</html>
"""

# 添加图片
fp = open('MotivationalQuote.jpeg', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()
msgImage.add_header('Content-ID', '<image1>')

# 挂载文本和HTML部分
part1 = MIMEText(text, "plain")
part2 = MIMEText(html, "html")
message_template.attach(msgImage)
message_template.attach(part1)
message_template.attach(part2)

# 建立一次SMTP连接,循环发送邮件
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", port, context=context) as server:
    server.login(sender_email, app_pass)
    for email in receiver_emails:
        # 每次创建新的邮件副本,避免污染原始模板
        message = MIMEMultipart("alternative")
        message.attach(message_template)
        message["Subject"] = message_template["Subject"]
        message["From"] = message_template["From"]
        # 设置当前收件人
        message["To"] = email
        # 发送邮件
        server.sendmail(sender_email, email, message.as_string())
关键修改点
  • 模板+副本模式:原始模板不设置To头,每次循环生成新的邮件副本,单独设置收件人,彻底避免多To头;
  • 单次连接登录:把SMTP连接和登录放在循环外,只执行一次,减少服务器请求开销;
  • 隔离原始对象:不直接修改原始MIME对象,防止头字段累积或其他意外副作用。
简化替代方案(删除旧To头)

如果不想创建副本,也可以在每次循环前删除原message的所有To头,再添加新的:

# ... 前面代码和你原始代码一致 ...

context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", port, context=context) as server:
    server.login(sender_email, app_pass)
    for email in receiver_emails:
        # 删除所有已存在的To头
        del message["To"]
        # 设置新的收件人
        message["To"] = email
        server.sendmail(sender_email, email, message.as_string())

这种方式更简洁,但如果邮件有其他动态内容,可能引发意外,模板+副本的方式更安全。

内容的提问来源于stack exchange,提问作者Abdul-Scripts

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 12:52:57