Python 内置了 smtplib 模块,它使得发送邮件变得非常简单,我们将从基础开始,逐步深入到更高级和实际的应用场景。

目录
- 核心概念: SMTP 是什么?
- 基础发送: 使用
smtplib和email发送一封简单的纯文本邮件。 - 进阶发送: 发送 HTML 邮件、添加附件。
- 使用 Gmail 发送邮件: 最常见的需求,包括处理安全性问题。
- 完整示例代码: 一个可运行的脚本,包含错误处理。
- 最佳实践与注意事项。
核心概念
- SMTP (Simple Mail Transfer Protocol): 用于发送电子邮件的标准协议,你的 Python 脚本将扮演一个“邮件客户端”的角色,通过 SMTP 协议与邮件服务器(如 Gmail, Outlook, 公司的邮件服务器)通信,然后将邮件传递给收件人。
- Python 标准库:
smtplib: 负责建立与 SMTP 服务器的连接,并执行发送邮件的命令(如登录、发送数据)。email: 一个功能强大的库,用于构建符合邮件标准的复杂邮件内容,包括主题、发件人、收件人、正文(纯文本/HTML)、附件等。- 重要提示: 你需要同时使用这两个库。
smtplib只负责“投递”,而email负责构建“包裹”。
基础发送:纯文本邮件
这是最简单的邮件发送方式,我们将发送一封没有附件、没有格式的纯文本邮件。
步骤:
-
导入必要的模块:
import smtplib from email.mime.text import MIMEText from email.header import Header
-
设置邮件内容:
subject: 邮件主题sender: 发件人邮箱receiver: 收件人邮箱 (可以是多个,用逗号分隔的字符串)body: 邮件正文
-
创建邮件对象:
(图片来源网络,侵删)MIMEText('邮件内容', 'plain', 'utf-8')创建一个纯文本邮件对象。'plain'表示纯文本,如果想用 HTML,则用'html'。'utf-8'是编码,确保中文等特殊字符能正确显示。
-
设置邮件头:
['From'],['To'],['Subject']是邮件的标准头信息。Header()用于处理非 ASCII 字符(如中文),防止乱码。
-
连接 SMTP 服务器并发送邮件:
smtplib.SMTP_SSL(host, port): 创建一个加密的 SSL 连接,现在大多数邮件服务商都推荐使用 SSL。server.login(): 登录你的邮箱。server.sendmail(): 发送邮件。server.quit(): 关闭连接。
示例代码:
import smtplib
from email.mime.text import MIMEText
from email.header import Header
# 1. 邮件内容配置
sender = 'your_email@example.com' # 替换为你的发件人邮箱
receivers = ['receiver1@example.com', 'receiver2@example.com'] # 替换为收件人邮箱列表
subject = 'Python SMTP 邮件测试'
body = '你好,\n\n这是一封由 Python SMTP 发送的测试邮件,\n\n祝好!'
# 2. 创建邮件对象
# 第一个参数是邮件正文,第二个参数是邮件类型,第三个参数是编码
message = MIMEText(body, 'plain', 'utf-8')
message['From'] = Header(sender) # 发件人
message['To'] = Header(','.join(receivers)) # 收件人,多个收件人用逗号分隔
message['Subject'] = Header(subject, 'utf-8') # 邮件主题
# 3. 连接服务器并发送邮件
try:
# 使用 SSL 加密连接,以 Gmail 为例
# host: SMTP 服务器地址
# port: SMTP 服务器端口 (SSL 通常为 465)
server = smtplib.SMTP_SSL('smtp.example.com', 465) # 替换为你的 SMTP 服务器地址和端口
# 如果需要登录,请取消下面两行的注释
# username = 'your_email@example.com'
# password = 'your_password' # 建议使用应用专用密码,而不是邮箱密码
# server.login(username, password)
server.sendmail(sender, receivers, message.as_string())
print("邮件发送成功!")
except smtplib.SMTPException as e:
print(f"邮件发送失败: {e}")
finally:
server.quit()
进阶发送:HTML 邮件和附件
发送带格式的 HTML 邮件和附件是更常见的需求,这需要用到 email.mime.multipart.MIMEMultipart 来创建一个可以包含多个部分的邮件。
示例代码:
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.header import Header
import os
# --- 邮件内容配置 ---
sender = 'your_email@example.com'
receivers = ['receiver@example.com']
subject = 'HTML 邮件与附件测试'
# --- 创建一个带有多部分的邮件对象 ---
msg = MIMEMultipart('alternative') # 'alternative' 表示正文有纯文本和HTML两种版本
msg['From'] = Header(sender)
msg['To'] = Header(','.join(receivers))
msg['Subject'] = Header(subject, 'utf-8')
# --- 1. 添加纯文本正文 (作为后备) ---
text_body = """
如果你正在阅读这封邮件,说明你的邮件客户端不支持 HTML 格式。
这是一封测试邮件。
"""
part1 = MIMEText(text_body, 'plain', 'utf-8')
msg.attach(part1)
# --- 2. 添加 HTML 正文 ---
html_body = """
<html>
<body>
<h1>你好,世界!</h1>
<p>这是一封 <b>HTML</b> 格式的测试邮件。</p>
<p>图片示例:<img src="cid:my_image_cid"></p>
</body>
</html>
"""
part2 = MIMEText(html_body, 'html', 'utf-8')
msg.attach(part2)
# --- 3. 添加附件 ---
# 假设你有一个名为 'report.pdf' 的文件在当前目录
attachment_path = 'report.pdf'
if os.path.exists(attachment_path):
with open(attachment_path, 'rb') as f:
# 创建附件对象
part3 = MIMEApplication(f.read(), Name=os.path.basename(attachment_path))
# 添加附件头信息
part3['Content-Disposition'] = f'attachment; filename="{os.path.basename(attachment_path)}"'
msg.attach(part3)
print(f"附件 '{attachment_path}' 已添加。")
else:
print(f"警告:附件文件 '{attachment_path}' 未找到,将跳过。")
# --- 发送邮件 ---
try:
server = smtplib.SMTP_SSL('smtp.example.com', 465) # 替换为你的SMTP服务器
# server.login(sender, 'your_password') # 如果需要登录
server.sendmail(sender, receivers, msg.as_string())
print("HTML邮件与附件发送成功!")
except smtplib.SMTPException as e:
print(f"邮件发送失败: {e}")
finally:
server.quit()
使用 Gmail 发送邮件
Gmail 是最常用的邮箱之一,但使用 Python 发送时需要注意几点:

- 启用“两步验证” (2-Step Verification): 如果你的账户开启了此功能,你将无法直接使用你的账户密码登录 SMTP 服务器。
- 生成“应用专用密码” (App Password):
- 登录你的 Google 账户。
- 进入“安全性”(Security) 设置。
- 在“两步验证”下方,找到“应用专用密码”(App passwords)。
- 选择应用为“邮件”(Mail),设备为“其他(自定义名称)”,并为其命名("Python Script")。
- Google 会生成一个 16 位的密码。请复制这个密码,它将是你 Python 脚本中使用的密码。
Gmail SMTP 服务器信息:
- 服务器地址:
smtp.gmail.com - SSL 端口:
465 - TLS 端口:
587(如果使用smtplib.SMTP(),然后调用starttls())
使用 Gmail 的示例代码:
import smtplib
from email.mime.text import MIMEText
from email.header import Header
sender = 'your_email@gmail.com'
receivers = ['receiver@example.com']
password = 'your_16_digit_app_password' # 在这里粘贴你的应用专用密码
subject = '来自 Gmail 的 Python 测试邮件'
body = '这封邮件是通过 Gmail SMTP 服务器发送的。'
message = MIMEText(body, 'plain', 'utf-8')
message['From'] = Header(sender)
message['To'] = Header(receivers[0]) # Gmail 收件人通常只写一个
message['Subject'] = Header(subject, 'utf-8')
try:
# 使用 SSL 连接 (推荐)
server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
server.login(sender, password)
server.sendmail(sender, receivers, message.as_string())
print("邮件通过 Gmail 发送成功!")
except smtplib.SMTPAuthenticationError:
print("发送失败!请检查邮箱地址和密码(确保是应用专用密码)。")
except smtplib.SMTPException as e:
print(f"邮件发送失败: {e}")
finally:
server.quit()
完整示例代码 (带配置和错误处理)
为了方便管理和复用,建议将配置信息(如服务器、密码)放在脚本外部或使用环境变量。
import smtplib
import os
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header
# --- 配置 (建议从环境变量读取,而不是硬编码) ---
SMTP_SERVER = os.getenv('SMTP_SERVER', 'smtp.example.com')
SMTP_PORT = int(os.getenv('SMTP_PORT', 465))
SMTP_USER = os.getenv('SMTP_USER', 'your_email@example.com')
SMTP_PASSWORD = os.getenv('SMTP_PASSWORD', 'your_password') # 或应用专用密码
def send_email(subject, body_html, body_text, sender, receivers, attachment_paths=None):
"""
发送一封支持 HTML 和附件的邮件
:param subject: 邮件主题
:param body_html: HTML 格式邮件正文
:param body_text: 纯文本格式邮件正文 (作为后备)
:param sender: 发件人邮箱
:param receivers: 收件人邮箱列表
:param attachment_paths: 附件文件路径列表
"""
msg = MIMEMultipart('alternative')
msg['Subject'] = Header(subject, 'utf-8')
msg['From'] = Header(sender)
msg['To'] = Header(','.join(receivers))
# 添加文本和HTML正文
msg.attach(MIMEText(body_text, 'plain', 'utf-8'))
msg.attach(MIMEText(body_html, 'html', 'utf-8'))
# 添加附件
if attachment_paths:
for path in attachment_paths:
if os.path.exists(path):
with open(path, 'rb') as f:
part = MIMEApplication(f.read(), Name=os.path.basename(path))
part['Content-Disposition'] = f'attachment; filename="{os.path.basename(path)}"'
msg.attach(part)
else:
print(f"警告: 附件文件 '{path}' 未找到。")
try:
print(f"正在连接服务器 {SMTP_SERVER}:{SMTP_PORT}...")
with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT) as server:
if SMTP_USER and SMTP_PASSWORD:
print("正在登录...")
server.login(SMTP_USER, SMTP_PASSWORD)
print(f"正在向 {receivers} 发送邮件...")
server.sendmail(sender, receivers, msg.as_string())
print("邮件发送成功!")
return True
except smtplib.SMTPAuthenticationError:
print("错误: 认证失败,请检查用户名和密码。")
except smtplib.SMTPException as e:
print(f"错误: 邮件发送失败 - {e}")
except Exception as e:
print(f"发生未知错误: {e}")
return False
# --- 使用示例 ---
if __name__ == '__main__':
# 设置你的收件人
my_receivers = ['friend1@example.com', 'friend2@example.com']
# 设置邮件内容
my_subject = "来自 Python 的问候"
my_html_body = """
<h2>你好!</h2>
<p>这是一封由 Python 自动发送的邮件。</p>
<p>希望你喜欢!</p>
"""
my_text_body = "你好!\n\n这是一封由 Python 自动发送的邮件,\n\n希望你喜欢!"
# 设置要附加的文件
# my_attachments = ['report.pdf', 'data.csv'] # 确保这些文件存在
# 调用函数发送邮件
send_email(
subject=my_subject,
body_html=my_html_body,
body_text=my_text_body,
sender=SMTP_USER,
receivers=my_receivers,
# attachment_paths=my_attachments
)
最佳实践与注意事项
-
安全性第一:
- 不要硬编码密码: 永远不要把邮箱密码直接写在代码里,使用环境变量、配置文件或密钥管理服务。
- 使用 SSL/TLS: 始终使用
SMTP_SSL或SMTP(...)+starttls()来加密你的连接,防止密码和邮件内容被窃听。 - 应用专用密码: 对于 Gmail 等服务,使用应用专用密码,而不是主账户密码。
-
处理大附件:
email.mime模块会将整个附件读入内存,如果附件非常大(几百MB),这可能会导致内存不足。- 对于超大附件,考虑使用专门的文件托管服务(如 Google Drive, Dropbox),然后在邮件中附上链接。
-
错误处理:
- 始终使用
try...except块来捕获smtplib.SMTPException及其子类(如SMTPAuthenticationError,SMTPRecipientsRefused等)。 - 检查文件是否存在再尝试作为附件发送。
- 始终使用
-
发送测试:
- 在开发时,先用自己的邮箱作为收件人进行测试。
- 注意不要频繁发送测试邮件,以免被邮件服务商标记为垃圾邮件发送者。
-
遵守法律法规:
- 确保你有权向收件人发送邮件,遵守反垃圾邮件法(如 CAN-SPAM Act)。
- 在邮件中提供退订链接是一个好习惯。
