Skip to content

Python 邮件发送

简单邮件传输协议(SMTP)是用于发送电子邮件的标准互联网协议。Python 的内置 smtplib 模块提供了客户端功能,用于连接到 SMTP 服务器并发送电子邮件。

为了健壮地构建电子邮件消息(处理头部信息、不同内容类型、附件),强烈推荐使用标准的 email.message 模块,而不是手动进行字符串格式化。

您可以使用 smtplib.SMTP 或 smtplib.SMTP_SSL(对于需要一开始就使用 SSL 连接的服务器)创建一个 SMTP 客户端会话对象。尽可能使用加密连接(SSL/TLS)至关重要。

import smtplib
# 选项 1: 连接标准 SMTP 端口,然后升级到 TLS
# smtp_server = smtplib.SMTP(host, port)
# smtp_server.starttls() # 保护连接
# 选项 2: 直接使用 SSL 连接
# smtp_server = smtplib.SMTP_SSL(host, port)
# Gmail 示例 (如果启用了两步验证,需要应用专用密码)
# host = 'smtp.gmail.com'
# port = 465 # 用于 SSL
# smtp_server = smtplib.SMTP_SSL(host, port)
# 本地调试服务器示例 (无加密)
# python -m smtpd -c DebuggingServer -n localhost:1025
# smtp_server = smtplib.SMTP('localhost', 1025)

SMTP() / SMTP_SSL() 的参数:

连接后,通常需要进行身份验证:

# smtp_server.login(sender_email, password)

安全提示: 避免在脚本中直接硬编码凭据。请使用环境变量、配置文件或安全的秘密管理工具。

使用 email.message.EmailMessage 类构建电子邮件结构。

import smtplib
import os
from email.message import EmailMessage
# --- 配置 (安全加载,例如从环境变量) ---
SMTP_HOST = 'smtp.example.com' # 替换为您的 SMTP 主机
SMTP_PORT = 587 # 替换为您的 SMTP 端口 (TLS)
SENDER_EMAIL = 'your_email@example.com' # 替换
SENDER_PASSWORD = os.environ.get('EMAIL_PASSWORD') # 示例: 从环境变量获取
RECEIVER_EMAIL = 'recipient@example.com' # 替换
# --------------------------------------------------------------------
if not SENDER_PASSWORD:
print("Error: 环境变量 EMAIL_PASSWORD 中未设置邮箱密码")
exit()
# 创建邮件消息对象
msg = EmailMessage()
msg['Subject'] = 'Test Email from Python'
msg['From'] = SENDER_EMAIL
msg['To'] = RECEIVER_EMAIL
msg.set_content('This is a plain text email sent using Python.')
try:
# 连接,登录,并发送
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
server.starttls() # 保护连接 (如果使用端口 587)
server.login(SENDER_EMAIL, SENDER_PASSWORD)
server.send_message(msg)
print("电子邮件发送成功!")
except smtplib.SMTPAuthenticationError:
print("认证失败。请检查邮箱/密码或应用专用密码设置。")
except smtplib.SMTPException as e:
print(f"发生 SMTP 错误: {e}")
except Exception as e:
print(f"发生错误: {e}")

关键步骤:

要发送 HTML 内容,使用 msg.add_alternative()。

import smtplib
import os
from email.message import EmailMessage
# --- 配置 (同上) ---
SMTP_HOST = 'smtp.example.com'
SMTP_PORT = 587
SENDER_EMAIL = 'your_email@example.com'
SENDER_PASSWORD = os.environ.get('EMAIL_PASSWORD')
RECEIVER_EMAIL = 'recipient@example.com'
# ----------------------------------
if not SENDER_PASSWORD:
print("Error: 未设置邮箱密码。")
exit()
msg = EmailMessage()
msg['Subject'] = 'HTML Email Test from Python'
msg['From'] = SENDER_EMAIL
msg['To'] = RECEIVER_EMAIL
# 设置纯文本内容 (客户端不支持 HTML 时的备用)
msg.set_content('这是纯文本版本。')
# 添加 HTML 备用
html_content = """
<html>
<body>
<p>这是一封 **HTML** 电子邮件。</p>
<h1>标题</h1>
<p>使用 Python 的 <code>smtplib</code> 和 <code>email.message</code> 模块发送。</p>
</body>
</html>
"""
msg.add_alternative(html_content, subtype='html')
try:
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
server.starttls()
server.login(SENDER_EMAIL, SENDER_PASSWORD)
server.send_message(msg)
print("HTML 电子邮件发送成功!")
except Exception as e:
print(f"发生错误: {e}")

add_alternative() 添加了相同内容的不同表示形式。电子邮件客户端通常会显示它们支持的最丰富格式(本例中是 HTML)。提供纯文本备用是良好的实践。

使用 msg.add_attachment() 包含文件。

import smtplib
import os
from email.message import EmailMessage
import mimetypes # 用于猜测文件类型
# --- 配置 (同上) ---
SMTP_HOST = 'smtp.example.com'
SMTP_PORT = 587
SENDER_EMAIL = 'your_email@example.com'
SENDER_PASSWORD = os.environ.get('EMAIL_PASSWORD')
RECEIVER_EMAIL = 'recipient@example.com'
# ----------------------------------
if not SENDER_PASSWORD:
print("Error: 未设置邮箱密码。")
exit()
ATTACHMENT_PATH = 'files/document.pdf' # 替换为您的文件路径
if not os.path.exists(ATTACHMENT_PATH):
print(f"Error: 未找到附件文件 {ATTACHMENT_PATH}")
exit()
msg = EmailMessage()
msg['Subject'] = 'Email with Attachment from Python'
msg['From'] = SENDER_EMAIL
msg['To'] = RECEIVER_EMAIL
msg.set_content('请查阅附件中的文档。')
# --- 添加附件 ---
# 猜测 MIME 类型
mime_type, _ = mimetypes.guess_type(ATTACHMENT_PATH)
if mime_type is None:
# 如果无法猜测类型,默认为通用二进制
mime_type = 'application/octet-stream'
maintype, subtype = mime_type.split('/', 1)
with open(ATTACHMENT_PATH, 'rb') as fp:
# 以二进制模式读取文件内容并添加为附件
msg.add_attachment(fp.read(),
maintype=maintype,
subtype=subtype,
filename=os.path.basename(ATTACHMENT_PATH))
# ---------------------
try:
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
server.starttls()
server.login(SENDER_EMAIL, SENDER_PASSWORD)
server.send_message(msg)
print("成功发送带附件的电子邮件!")
except Exception as e:
print(f"发生错误: {e}")

附件的关键步骤: