Python使用smtplib连接Office365发邮件报SMTPRecipientsRefused 421错误解决
报错根源定位
这个报错是Office365 SMTP服务端的主动拦截:当单条连接内累计出现6次以上协议合规校验失败时,服务端会直接关闭传输通道,和smtplib本身的功能限制无关。对应你代码和业务场景的核心触发点:
- 收件人格式不符合SMTP协议规范
你直接把分号分隔的8个邮箱写入msg['To']字段,但SMTP协议要求RCPT TO指令的收件人分隔符必须是逗号,不支持分号。Office365网关会对每个识别到的收件人做格式校验,分号分隔的长串内容会被判定为无效地址,每出现一个无效地址就累计1次协议错误,8个地址的错误计数刚好突破6次的断连阈值。你测试3个收件人时流程正常,本质是错误计数没达到触发断连的阈值,不是收件人数量本身的问题。 - 高频重复建立SMTP连接
你在循环内部每发1封邮件就完成一次建连、TLS握手、登录、断开全流程,70组记录就要发起70次重复认证连接。短时间内的高频重复建连行为会被Office365的反滥用规则判定为异常流量,直接累计为协议错误计数。 - 发件人字段与登录账号不匹配
你将表格中读取的tosendfrom地址赋值给msg['From'],但实际SMTP认证用的是固定的xxx@xxx.com账号。Office365默认禁止无授权的发件人地址伪造,一旦From字段和认证账号不一致,就会触发协议合规校验失败,累计错误计数。
针对性修复方案
- 统一清洗收件人格式
拿到原始的分号分隔收件人字符串后,做标准化处理:将分号替换为SMTP要求的逗号分隔格式,拆分后逐个去掉前后空格、过滤空字符串,再组装为合法的收件人列表赋值给邮件头,示例代码:raw_recipients = Emaitstosendto # 分号转逗号,拆分清洗 recipient_list = [addr.strip() for addr in raw_recipients.replace(";", ",").split(",") if addr.strip()] email_to = ",".join(recipient_list) - 修正发件人配置
将msg['From']的取值改为与SMTP登录完全一致的认证账号,不要随意替换为表格内的其他发件人地址。如果需要留存不同的回复目标,可以通过设置Reply-To字段实现,避免直接修改From字段触发拦截。 - 单次连接批量复用发送
将SMTP建连、TLS初始化、登录的逻辑移到循环外部,整个批量发送流程只初始化一次连接,发完所有邮件再统一断开,避免每发一封就重复建连触发的异常计数。调整后的核心结构:# 循环外完成一次连接初始化 with smtplib.SMTP(smtp_ssl_host, smtp_ssl_port) as s2: s2.starttls(context=context) s2.login(email_login, email_pass) # 遍历记录的逻辑全部移到连接块内部 for i in range(len(emails_to)): # 原有邮件组装逻辑 s2.send_message(msg) # 连接块退出时自动安全断开 - 适配发送频率限制
每发送10封邮件增加1-2秒的延时,匹配Office365单连接每分钟最多30封邮件的频率限制,避免触发421限流。
内容的提问来源于stack exchange,提问作者Herdil
相关产品推荐
相关产品推荐

