pin_drop当前位置:知识文库 ❯ 图文
Python smtplib邮件发送教程 - 附件与HTML邮件实战
目录
一、SMTP 协议简介
SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)是用于发送电子邮件的标准协议,定义在 RFC 5321 中。它使用 TCP 端口 25(非加密)、465(SSL)或 587(STARTTLS)进行通信。
邮件发送的基本流程:
-
连接服务器:客户端与 SMTP 服务器建立 TCP 连接
-
身份认证:发送用户名和密码(或授权码)
-
指定发件人:MAIL FROM 命令
-
指定收件人:RCPT TO 命令
-
发送邮件内容:DATA 命令传输邮件头和正文
-
断开连接:QUIT 命令
Python 标准库中的 smtplib 模块封装了 SMTP 协议的完整实现,配合 email 模块可以轻松构建和发送邮件。
二、smtplib 基本用法
核心类与方法
代码示例
import smtplib
from email.mime.text import MIMEText
from email.header import Header
# 1. 创建 SMTP 连接(非加密,仅用于演示)
smtp = smtplib.SMTP('smtp.example.com', 25)
# 2. 登录认证
smtp.login('sender@example.com', 'your_password')
# 3. 构建邮件
msg = MIMEText('Hello, this is a test email.', 'plain', 'utf-8')
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg['Subject'] = Header('测试邮件', 'utf-8')
# 4. 发送邮件
smtp.sendmail('sender@example.com', ['recipient@example.com'], msg.as_string())
# 5. 关闭连接
smtp.quit()
安全提示:生产环境中应始终使用 SSL/TLS 加密连接。推荐使用
SMTP_SSL或starttls()方法。切勿在代码中硬编码密码,使用环境变量或配置文件管理凭据。
三、发送纯文本邮件
代码示例
import smtplib
from email.mime.text import MIMEText
from email.header import Header
import os
# 邮件配置(建议使用环境变量存储敏感信息)
SMTP_SERVER = 'smtp.qq.com'
SMTP_PORT = 465
SENDER_EMAIL = os.environ.get('SENDER_EMAIL', 'your_email@qq.com')
SENDER_PASSWORD = os.environ.get('SENDER_PASSWORD', 'your_auth_code')
RECEIVER_EMAIL = 'recipient@example.com'
# 构建纯文本邮件
content = """你好!
这是一封使用 Python smtplib 发送的测试邮件。
邮件功能演示:
- 纯文本内容
- 中文编码支持
- SSL 加密传输
祝好!
Python 邮件发送教程"""
msg = MIMEText(content, 'plain', 'utf-8')
msg['From'] = Header(f'Python教程 <{SENDER_EMAIL}>', 'utf-8')
msg['To'] = Header('测试收件人', 'utf-8')
msg['Subject'] = Header('【测试】Python smtplib 纯文本邮件', 'utf-8')
# 发送 SSL 加密邮件
try:
with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT) as smtp:
smtp.login(SENDER_EMAIL, SENDER_PASSWORD)
smtp.sendmail(SENDER_EMAIL, [RECEIVER_EMAIL], msg.as_string())
print('邮件发送成功!')
except smtplib.SMTPException as e:
print(f'邮件发送失败:{e}')
四、发送 HTML 格式邮件
HTML 邮件可以包含丰富的格式、链接、图片等,更适合通知类和营销类邮件:
代码示例
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
import os
SENDER_EMAIL = os.environ.get('SENDER_EMAIL', 'your_email@qq.com')
SENDER_PASSWORD = os.environ.get('SENDER_PASSWORD', 'your_auth_code')
RECEIVER_EMAIL = 'recipient@example.com'
# 创建多部分邮件容器
msg = MIMEMultipart('alternative')
msg['From'] = Header(f'系统通知 <{SENDER_EMAIL}>', 'utf-8')
msg['To'] = RECEIVER_EMAIL
msg['Subject'] = Header('【通知】您的订单已发货', 'utf-8')
# 纯文本备选内容
text_part = MIMEText('您的订单 #20240101 已发货,预计3天内送达。', 'plain', 'utf-8')
# HTML 正文
html_content = """
<html>
<body style="font-family: Arial, sans-serif; color: #333;">
<div style="max-width: 600px; margin: 0 auto; padding: 20px;">
<h2 style="color: #2196F3;">订单发货通知</h2>
<p>尊敬的客户:</p>
<p>您的订单 <strong style="color: #d63384;">#20240101</strong> 已经发货!</p>
<table style="width: 100%; border-collapse: collapse; margin: 20px 0;">
<tr style="background: #f8f9fa;">
<td style="padding: 10px; border: 1px solid #ddd;">商品</td>
<td style="padding: 10px; border: 1px solid #ddd;">Python 编程教程</td>
</tr>
<tr>
<td style="padding: 10px; border: 1px solid #ddd;">物流单号</td>
<td style="padding: 10px; border: 1px solid #ddd;">SF1234567890</td>
</tr>
<tr style="background: #f8f9fa;">
<td style="padding: 10px; border: 1px solid #ddd;">预计送达</td>
<td style="padding: 10px; border: 1px solid #ddd;">3天内</td>
</tr>
</table>
<a href="https://example.com/track" style="
display: inline-block;
background: #2196F3;
color: white;
padding: 12px 24px;
text-decoration: none;
border-radius: 5px;
">查看物流详情</a>
<p style="margin-top: 30px; color: #888; font-size: 12px;">
此邮件为系统自动发送,请勿直接回复。
</p>
</div>
</body>
</html>
"""
html_part = MIMEText(html_content, 'html', 'utf-8')
# 添加两个版本(邮件客户端优先显示 HTML)
msg.attach(text_part)
msg.attach(html_part)
# 发送邮件
with smtplib.SMTP_SSL('smtp.qq.com', 465) as smtp:
smtp.login(SENDER_EMAIL, SENDER_PASSWORD)
smtp.sendmail(SENDER_EMAIL, [RECEIVER_EMAIL], msg.as_string())
print('HTML 邮件发送成功!')
五、添加附件
使用 MIMEBase 和 encoders 可以为邮件添加附件:
代码示例
import smtplib
import os
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
from email.header import Header
SENDER_EMAIL = os.environ.get('SENDER_EMAIL', 'your_email@qq.com')
SENDER_PASSWORD = os.environ.get('SENDER_PASSWORD', 'your_auth_code')
RECEIVER_EMAIL = 'recipient@example.com'
# 创建邮件容器
msg = MIMEMultipart()
msg['From'] = Header(f'{SENDER_EMAIL}', 'utf-8')
msg['To'] = RECEIVER_EMAIL
msg['Subject'] = Header('项目报告 - 附件', 'utf-8')
# 添加正文
msg.attach(MIMEText('请查收附件中的项目报告。', 'plain', 'utf-8'))
# 添加文件附件
def add_attachment(msg, file_path):
"""添加附件到邮件"""
with open(file_path, 'rb') as f:
# 读取文件内容
attachment = MIMEBase('application', 'octet-stream')
attachment.set_payload(f.read())
# Base64 编码
encoders.encode_base64(attachment)
# 设置文件名(支持中文)
filename = os.path.basename(file_path)
attachment.add_header(
'Content-Disposition',
'attachment',
filename=('utf-8', '', filename)
)
msg.attach(attachment)
# 添加多个不同类型的附件
add_attachment(msg, 'report.pdf')
add_attachment(msg, 'data.xlsx')
add_attachment(msg, 'photo.jpg')
# 发送
with smtplib.SMTP_SSL('smtp.qq.com', 465) as smtp:
smtp.login(SENDER_EMAIL, SENDER_PASSWORD)
smtp.sendmail(SENDER_EMAIL, [RECEIVER_EMAIL], msg.as_string())
print('带附件邮件发送成功!')
六、群发邮件与抄送
设置抄送(CC)和密送(BCC)
代码示例
import smtplib
import os
from email.mime.text import MIMEText
from email.header import Header
SENDER_EMAIL = os.environ.get('SENDER_EMAIL', 'your_email@qq.com')
SENDER_PASSWORD = os.environ.get('SENDER_PASSWORD', 'your_auth_code')
# 收件人列表
to_recipients = ['user1@example.com', 'user2@example.com']
cc_recipients = ['manager@example.com']
bcc_recipients = ['archive@example.com']
# 所有收件人(用于 SMTP 发送)
all_recipients = to_recipients + cc_recipients + bcc_recipients
msg = MIMEText('大家好,这是本周的项目进度报告。', 'plain', 'utf-8')
msg['From'] = Header(SENDER_EMAIL, 'utf-8')
msg['To'] = ', '.join(to_recipients)
msg['Cc'] = ', '.join(cc_recipients) # 抄送显示在邮件头
msg['Subject'] = Header('【周报】项目进度报告', 'utf-8')
# 注意:BCC 不在邮件头中显示
with smtplib.SMTP_SSL('smtp.qq.com', 465) as smtp:
smtp.login(SENDER_EMAIL, SENDER_PASSWORD)
smtp.sendmail(SENDER_EMAIL, all_recipients, msg.as_string())
print(f'邮件已发送给 {len(all_recipients)} 个收件人')
批量发送个性化邮件
代码示例
import smtplib
import os
from email.mime.text import MIMEText
from email.header import Header
def send_personalized_emails(recipients, subject_template, body_template):
"""批量发送个性化邮件"""
SENDER_EMAIL = os.environ.get('SENDER_EMAIL', 'your_email@qq.com')
SENDER_PASSWORD = os.environ.get('SENDER_PASSWORD', 'your_auth_code')
with smtplib.SMTP_SSL('smtp.qq.com', 465) as smtp:
smtp.login(SENDER_EMAIL, SENDER_PASSWORD)
for name, email in recipients:
# 个性化内容
subject = subject_template.format(name=name)
body = body_template.format(name=name)
msg = MIMEText(body, 'plain', 'utf-8')
msg['From'] = Header(SENDER_EMAIL, 'utf-8')
msg['To'] = email
msg['Subject'] = Header(subject, 'utf-8')
try:
smtp.sendmail(SENDER_EMAIL, [email], msg.as_string())
print(f'已发送给 {name} ({email})')
except smtplib.SMTPException as e:
print(f'发送给 {name} 失败:{e}')
# 使用示例
recipients = [
('张三', 'zhangsan@example.com'),
('李四', 'lisi@example.com'),
('王五', 'wangwu@example.com'),
]
send_personalized_emails(
recipients=recipients,
subject_template='你好 {name},这是一封个性化邮件',
body_template='亲爱的 {name}:\n\n感谢你的支持!\n\n祝好!'
)
七、SSL/TLS 安全连接
现代邮件服务器都要求加密连接。smtplib 支持两种加密方式:
代码示例
import smtplib
import os
SENDER_EMAIL = os.environ.get('SENDER_EMAIL', 'your_email@qq.com')
SENDER_PASSWORD = os.environ.get('SENDER_PASSWORD', 'your_auth_code')
# 方式一:SMTP_SSL(推荐,更简洁)
with smtplib.SMTP_SSL('smtp.qq.com', 465) as smtp:
smtp.login(SENDER_EMAIL, SENDER_PASSWORD)
print('SSL 连接成功')
# 方式二:SMTP + starttls
with smtplib.SMTP('smtp.qq.com', 587) as smtp:
smtp.ehlo() # 标识客户端
smtp.starttls() # 升级为加密连接
smtp.ehlo() # 重新标识
smtp.login(SENDER_EMAIL, SENDER_PASSWORD)
print('STARTTLS 连接成功')
八、常见邮箱服务器配置
提示:QQ 邮箱和 163 邮箱等国内邮箱服务需要开启 SMTP 功能并获取授权码,不能直接使用登录密码。Gmail 需要开启两步验证后生成应用专用密码。
九、小结与练习题
核心要点回顾
-
SMTP 协议:标准邮件发送协议,使用 TCP 端口 465(SSL)或 587(STARTTLS)
-
MIMEText:构建纯文本或 HTML 邮件内容
-
MIMEMultipart:支持多部分内容(HTML + 纯文本 + 附件)
-
MIMEBase:用于添加附件,需要 Base64 编码
-
安全连接:始终使用 SMTP_SSL 或 starttls() 加密传输
-
授权码:国内邮箱需使用授权码而非登录密码
练习1
编写一个函数 send_email(to, subject, body, attachments=None),封装邮件发送功能,支持纯文本/HTML 正文和可选的附件列表。要求使用环境变量获取凭据,并返回发送是否成功的布尔值。
练习2
编写一个邮件通知系统,从 CSV 文件中读取收件人列表,为每个收件人生成个性化的 HTML 邮件(包含收件人姓名、订单信息),并附带 PDF 格式的发票文件。发送失败时记录日志并继续发送下一封。
常见问题
为什么邮件发送失败并提示 "Authentication failed"?
最常见原因是使用了登录密码而非授权码。QQ 邮箱、163 邮箱等需要在邮箱设置中开启 SMTP 功能并生成专属授权码。Gmail 需要开启两步验证并创建应用专用密码。此外,请确认 SMTP 服务器地址和端口号正确。
附件中文文件名乱码如何解决?
使用 attachment.add_header('Content-Disposition', 'attachment', filename=('utf-8', '', filename)) 的三元组格式,可以正确处理中文文件名。这是 RFC 2231 标准推荐的方式。
如何内嵌图片到 HTML 邮件中?
使用 MIMEMultipart('related') 创建邮件容器,将图片作为 MIMEImage 添加并设置 Content-ID 头,然后在 HTML 中使用 引用。
如何避免邮件被识别为垃圾邮件?
1) 设置正确的 From、To、Subject 头信息;2) 避免过多的 HTML 标签和花哨的样式;3) 同时提供纯文本和 HTML 版本;4) 不要短时间内大量发送邮件;5) 确保域名有正确的 SPF、DKIM、DMARC 记录;6) 提供退订链接。
本文涉及AI创作
内容由AI创作,请仔细甄别