XML作为数据交换的常用格式,经常出现在接口日志、配置备份、数据报表等场景中。当系统需要把这些XML文件定时推送给相关人员时,用Python的smtplib配合email模块是最轻量的方案之一,不需要安装任何第三方库,标准库就能搞定。本文从原理到代码完整讲解如何把一个或多个XML文件作为附件发送出去。

一、发送附件的核心原理:MIME多部分消息结构
要理解附件是怎么发出去的,得先明白邮件的组成结构。一封普通的纯文本邮件,只需要一个简单的正文即可;但一旦带上附件,邮件就必须变成MIME多部分消息(Multipart)。所谓Multipart,就是把邮件拆成多个独立的部分,每一段用边界字符串分隔,每一段有自己的类型声明。
在Python中,这个结构由email.mime.multipart.MIMEMultipart来表示。我们把正文文本作为一个子部分(MIMEText)加进去,再把每个XML文件作为一个二进制附件部分(MIMEApplication或MIMEBase)加进去,最终生成一封完整的多部分邮件。接收方的邮件客户端会根据每部分的Content-Type和Content-Disposition头,自动识别哪个是正文、哪个是附件。
XML文件本质上也是文本,理论上可以用MIMEText发送,但更规范的做法是把它当作通用二进制流处理,使用MIMEApplication,并显式设置Content-Disposition为attachment,这样各种邮件客户端的兼容性最好,不会出现XML内容被直接渲染在正文里的情况。
二、完整代码:单个XML文件作为附件发送
下面是一份可以直接运行的完整代码,使用163邮箱的SMTP服务作为示例。运行前需要先在邮箱设置里开启SMTP服务,并获取授权码(不是登录密码),这是国内主流邮箱的通用做法。
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
from email.header import Header
from email.utils import formataddr
# SMTP服务器配置(以163邮箱为例)
SMTP_SERVER = "smtp.163.com"
SMTP_PORT = 465 # SSL加密端口
SENDER = "your_account@163.com"
AUTH_CODE = "你的SMTP授权码"
RECEIVER = "target@example.net"
def send_xml_email(xml_path, subject, body):
# 构建多部分邮件对象
msg = MIMEMultipart()
msg["Subject"] = Header(subject, "utf-8")
msg["From"] = formataddr(("数据系统", SENDER))
msg["To"] = RECEIVER
# 添加正文
msg.attach(MIMEText(body, "plain", "utf-8"))
# 读取XML文件并添加为附件
with open(xml_path, "rb") as f:
attachment = MIMEApplication(f.read())
# 指定附件文件名,UTF-8编码防止中文乱码
attachment.add_header(
"Content-Disposition",
"attachment",
filename=("utf-8", "", "订单数据.xml")
)
msg.attach(attachment)
# 建立SSL加密连接并发送
try:
with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT, timeout=15) as server:
server.login(SENDER, AUTH_CODE)
server.sendmail(SENDER, [RECEIVER], msg.as_string())
print("邮件发送成功")
except smtplib.SMTPException as e:
print(f"邮件发送失败:{e}")
send_xml_email("D:\\data\\order.xml", "每日订单数据", "您好,附件是今日订单XML数据,请查收。")代码中有几个关键点值得展开说明。第一,open(xml_path, "rb")必须以二进制模式读取,因为MIMEApplication要求传入bytes类型的数据,如果用文本模式读取,遇到XML声明中的编码不一致就会抛出解码异常。第二,文件名部分用了三元素元组("utf-8", "", "订单数据.xml"),这是RFC 2231的标准写法,能保证中文文件名在Outlook、Foxmail等客户端中正常显示,如果直接写filename="订单数据.xml",部分客户端会显示成乱码或被截断。
第三,端口选择上要区分两种情况:465端口走SMTP_SSL,属于一开始就建立加密通道;而587端口通常配合starttls()使用,先建立明文连接再升级为加密。如果你的邮箱服务商只支持587,把SMTP_SSL换成smtplib.SMTP再调用一次server.starttls()即可。
三、批量发送多个XML附件与常见坑
实际业务中往往一次要发多个XML文件,比如把某个目录下当天生成的所有报表一起打包发送。实现思路很简单:遍历文件列表,循环创建MIMEApplication对象并逐个attach即可。
import os
from pathlib import Path
def attach_xml_files(msg, folder):
# 遍历目录,把所有XML文件都加为附件
xml_files = Path(folder).glob("*.xml")
for xml_file in xml_files:
with open(xml_file, "rb") as f:
part = MIMEApplication(f.read())
part.add_header(
"Content-Disposition",
"attachment",
filename=("utf-8", "", xml_file.name)
)
msg.attach(part)
# 使用方式:attach_xml_files(msg, r"C:\reports\daily")批量发送时有几个容易踩的坑需要留意。首先是附件总大小限制,绝大多数邮箱服务商限制单封邮件不超过50MB,如果XML文件数量多体积大,建议先用zipfile压缩成一个压缩包再作为附件发送,体积能缩小不少。其次是文件重名问题,如果不同子目录里存在同名XML,直接用文件名做附件名会冲突,可以在文件名前拼接日期或序号加以区分。
另一个高频问题是授权码被拒绝登录,报错信息通常是SMTPAuthenticationError。原因多是账号在邮箱后台没有开启SMTP服务,或者把登录密码当成授权码用了。以163邮箱为例,必须在网页版设置中找到POP3/SMTP/IMAP选项,开启服务后会生成一串授权码,代码中要填的就是这串字符。此外,如果部署在云服务器上发送,部分服务商要求将服务器IP加入白名单,否则会直接拒绝连接。
最后是稳定性方面的建议。生产环境中发邮件最好加上重试机制,比如用循环配合time.sleep做三次重试;同时给SMTP_SSL设置timeout参数,避免网络抖动时程序无限阻塞。对于定时任务场景,可以把发送结果写入日志,方便排查漏发问题。
总结一下,用Python发送XML附件邮件的核心步骤就是:构建MIMEMultipart、附加正文、以二进制方式读取XML并attach、通过SMTP_SSL登录发送。把这套代码封装成函数,再配合定时任务框架,就能轻松实现XML数据的自动化邮件推送。
smtplib发送附件XML文件传输Python邮件编程修改时间:2026-09-05 03:34:31