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_SSLstarttls() 方法。切勿在代码中硬编码密码,使用环境变量或配置文件管理凭据。


三、发送纯文本邮件

代码示例

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 邮件发送成功!')

五、添加附件

使用 MIMEBaseencoders 可以为邮件添加附件:

代码示例

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 支持两种加密方式:

对比项 SMTP_SSL SMTP + starttls()
端口 465 587
加密时机 连接时立即加密 连接后升级为加密
使用方式 SMTP_SSL(host, 465) smtp.starttls()
推荐度 ★★★★★ 简单直接 ★★★★☆ 更灵活

代码示例

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 连接成功')

八、常见邮箱服务器配置

邮箱服务 SMTP 服务器 SSL 端口 认证方式
QQ 邮箱 smtp.qq.com 465 授权码
163 邮箱 smtp.163.com 465 授权码
Gmail smtp.gmail.com 465 应用专用密码
Outlook smtp-mail.outlook.com 587 密码 / OAuth2
企业邮箱 smtp.exmail.qq.com 465 密码

提示: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) 提供退订链接。

标签: Python smtplib 邮件发送 SMTP 邮件附件 网络编程

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Python RESTful API调用教程 - JSON交互与认证 下一篇: Python进程与线程概念详解 - 并发与并行入门指南

poll相关推荐