使用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
相关产品推荐
相关产品推荐

