文章目录

Python邮件发送全场景实现:核心库解析、代码示例与最佳实践

在Python开发中,邮件发送是常见需求,涵盖验证码推送、告警通知、报表投递等场景。Python提供了内置标准库smtplib+email)实现底层邮件协议交互,也有第三方库(如yagmail)简化开发流程。本文系统梳理主流邮件发送库的功能、使用方法、实战代码及注意事项,帮助开发者快速实现各类邮件发送需求。

一、核心库与功能解析

Python发送邮件的核心依赖两类工具:标准库(原生支持,无需额外安装)和第三方库(简化代码,提升效率),其功能与适用场景如下:

库/工具组合 核心功能 优势 劣势 适用场景
smtplib(标准库) 实现SMTP协议(简单邮件传输协议),负责建立连接、身份验证、发送邮件 原生支持,灵活可控,无依赖 需手动构造邮件结构(如收件人、附件) 需定制化邮件(如带复杂附件、HTML格式)
email(标准库) 构造邮件内容,支持纯文本、HTML、附件、图片等格式 支持所有邮件结构,可高度定制 代码繁琐,需手动组装MIME对象 复杂邮件场景(多附件、内嵌图片)
yagmail(第三方) 封装smtplibemail,提供极简API 代码量减少80%,自动处理连接与格式 定制化能力弱于标准库,需额外安装 快速开发(如验证码、简单通知)

二、环境准备:开启SMTP服务与获取授权码

发送邮件前需先配置发件人邮箱的SMTP服务(非登录密码,需获取“授权码”,避免暴露邮箱密码),主流邮箱的配置步骤如下:

1. 主流邮箱SMTP配置信息

邮箱类型 SMTP服务器地址 端口(SSL加密) 开启SMTP路径
QQ邮箱 smtp.qq.com 465 邮箱设置 → 账户 → 开启“POP3/SMTP服务”
163邮箱 smtp.163.com 465 设置 → POP3/SMTP/IMAP → 开启SMTP
Gmail smtp.gmail.com 465 账户 → 安全 → 开启“两步验证”→ 生成应用密码
企业微信邮箱 smtp.exmail.qq.com 465 管理中心 → 账户 → 开启SMTP服务

2. 获取授权码(以QQ邮箱为例)

  1. 登录QQ邮箱 → 点击顶部“设置” → 进入“账户”页面;
  2. 下拉找到“POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务”;
  3. 开启“POP3/SMTP服务”,按提示发送短信验证;
  4. 验证通过后,系统生成16位授权码(保存此授权码,用于代码登录)。

三、实战代码示例:覆盖全场景需求

以下按“基础到复杂”的顺序,提供不同场景的代码示例,涵盖纯文本、HTML格式、带附件、第三方库简化发送等需求。

场景1:发送纯文本邮件(标准库smtplib+email

适用于简单通知(如系统告警、验证码),仅包含文本内容。

import smtplib
from email.mime.text import MIMEText
from email.header import Header

def send_text_email(sender, auth_code, receivers, subject, content):
    """
    发送纯文本邮件
    :param sender: 发件人邮箱(如 "xxx@qq.com")
    :param auth_code: 邮箱授权码(非登录密码)
    :param receivers: 收件人列表(如 ["yyy@163.com", "zzz@gmail.com"])
    :param subject: 邮件主题
    :param content: 邮件正文(纯文本)
    """
    # 1. 构造纯文本邮件对象(_subtype="plain"表示纯文本,编码用utf-8避免中文乱码)
    msg = MIMEText(content, _subtype="plain", _charset="utf-8")
    # 设置邮件头:发件人、收件人、主题
    msg["From"] = Header(sender, "utf-8")  # 发件人显示名(可自定义,如 "系统通知<xxx@qq.com>")
    msg["To"] = Header(", ".join(receivers), "utf-8")  # 收件人(多个用逗号分隔)
    msg["Subject"] = Header(subject, "utf-8")  # 邮件主题

    try:
        # 2. 连接SMTP服务器(以QQ邮箱为例,端口465用SSL加密)
        smtp_obj = smtplib.SMTP_SSL("smtp.qq.com", 465)
        # 3. 登录邮箱(用户名=发件人邮箱,密码=授权码)
        smtp_obj.login(sender, auth_code)
        # 4. 发送邮件(from_addr=发件人,to_addrs=收件人列表,msg.as_string()=邮件内容字符串)
        smtp_obj.sendmail(sender, receivers, msg.as_string())
        print("纯文本邮件发送成功!")
    except smtplib.SMTPException as e:
        print(f"纯文本邮件发送失败:{e}")
    finally:
        # 5. 关闭连接
        smtp_obj.quit()

# ------------------- 调用示例 -------------------
if __name__ == "__main__":
    SENDER = "your_email@qq.com"  # 替换为你的发件人邮箱
    AUTH_CODE = "your_auth_code"  # 替换为你的授权码
    RECEIVERS = ["recipient1@163.com", "recipient2@gmail.com"]  # 替换为收件人邮箱
    SUBJECT = "Python纯文本邮件测试"
    CONTENT = "这是一封由Python smtplib库发送的纯文本邮件,用于测试基础邮件发送功能。"
    
    send_text_email(SENDER, AUTH_CODE, RECEIVERS, SUBJECT, CONTENT)

场景2:发送HTML格式邮件(含样式与链接)

适用于美观的通知(如营销邮件、报表通知),支持HTML标签(如<h1><a><table>)与内联样式。

import smtplib
from email.mime.text import MIMEText
from email.header import Header

def send_html_email(sender, auth_code, receivers, subject, html_content):
    """
    发送HTML格式邮件
    :param html_content: 邮件正文(HTML字符串)
    """
    # 1. 构造HTML邮件对象(_subtype="html"表示HTML格式)
    msg = MIMEText(html_content, _subtype="html", _charset="utf-8")
    msg["From"] = Header(f"Python邮件测试<{sender}>", "utf-8")
    msg["To"] = Header(", ".join(receivers), "utf-8")
    msg["Subject"] = Header(subject, "utf-8")

    try:
        smtp_obj = smtplib.SMTP_SSL("smtp.qq.com", 465)
        smtp_obj.login(sender, auth_code)
        smtp_obj.sendmail(sender, receivers, msg.as_string())
        print("HTML邮件发送成功!")
    except smtplib.SMTPException as e:
        print(f"HTML邮件发送失败:{e}")
    finally:
        smtp_obj.quit()

# ------------------- 调用示例 -------------------
if __name__ == "__main__":
    # HTML内容(支持内联样式和链接)
    HTML_CONTENT = """
    <html>
      <body>
        <h2 style="color:#165DFF;">Python HTML邮件测试</h2>
        <p>这是一封带HTML格式的邮件,支持以下功能:</p>
        <ul style="color:#333;">
          <li>字体颜色与大小设置</li>
          <li>超链接:<a href="https://www.python.org" target="_blank">Python官网</a></li>
          <li>表格展示:</li>
        </ul>
        <table border="1" cellpadding="5" style="border-collapse:collapse;">
          <tr style="background:#f5f5f5;">
            <th>序号</th>
            <th>功能</th>
          </tr>
          <tr>
            <td>1</td>
            <td>HTML格式渲染</td>
          </tr>
          <tr>
            <td>2</td>
            <td>内联样式支持</td>
          </tr>
        </table>
      </body>
    </html>
    """
    send_html_email(
        sender="your_email@qq.com",
        auth_code="your_auth_code",
        receivers=["recipient@163.com"],
        subject="Python HTML邮件测试",
        html_content=HTML_CONTENT
    )

场景3:发送带附件的邮件(支持多附件)

适用于需要传输文件的场景(如报表Excel、日志文件、图片),需使用MIMEMultipart组装“邮件正文+附件”。

import smtplib
import os
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.header import Header
from email import encoders

def send_attachment_email(sender, auth_code, receivers, subject, content, attachment_paths):
    """
    发送带附件的邮件
    :param attachment_paths: 附件路径列表(如 ["report.xlsx", "log.txt"])
    :param content: 邮件正文(纯文本或HTML字符串)
    """
    # 1. 创建带附件的邮件对象(MIMEMultipart()表示多部分邮件)
    msg = MIMEMultipart()
    msg["From"] = Header(f"附件邮件<{sender}>", "utf-8")
    msg["To"] = Header(", ".join(receivers), "utf-8")
    msg["Subject"] = Header(subject, "utf-8")

    # 2. 添加邮件正文(先判断是否为HTML格式)
    if "<html>" in content:
        msg.attach(MIMEText(content, "html", "utf-8"))
    else:
        msg.attach(MIMEText(content, "plain", "utf-8"))

    # 3. 逐个添加附件
    for file_path in attachment_paths:
        if not os.path.exists(file_path):
            print(f"附件 {file_path} 不存在,跳过添加")
            continue
        
        # 3.1 读取附件文件(二进制模式)
        with open(file_path, "rb") as f:
            # 3.2 创建附件对象(MIMEBase类型,主类型为"application",子类型为"octet-stream"表示二进制流)
            attachment = MIMEBase("application", "octet-stream")
            attachment.set_payload(f.read())  # 设置附件内容
        
        # 3.3 编码附件(避免中文文件名乱码)
        encoders.encode_base64(attachment)
        # 3.4 设置附件头(filename为附件显示名)
        filename = os.path.basename(file_path)
        attachment.add_header(
            "Content-Disposition",
            f"attachment; filename={Header(filename, 'utf-8').encode()}"
        )
        # 3.5 将附件添加到邮件对象
        msg.attach(attachment)

    try:
        smtp_obj = smtplib.SMTP_SSL("smtp.qq.com", 465)
        smtp_obj.login(sender, auth_code)
        smtp_obj.sendmail(sender, receivers, msg.as_string())
        print("带附件邮件发送成功!")
    except smtplib.SMTPException as e:
        print(f"带附件邮件发送失败:{e}")
    finally:
        smtp_obj.quit()

# ------------------- 调用示例 -------------------
if __name__ == "__main__":
    # 附件路径(替换为你的文件路径,支持多个附件)
    ATTACHMENTS = ["test_report.xlsx", "system_log.txt"]
    send_attachment_email(
        sender="your_email@qq.com",
        auth_code="your_auth_code",
        receivers=["recipient@163.com"],
        subject="Python带附件邮件测试",
        content="这是一封带附件的邮件,包含测试报表和系统日志。",
        attachment_paths=ATTACHMENTS
    )

场景4:用yagmail简化发送(第三方库)

yagmail通过封装smtplibemail,将邮件发送代码简化至3行以内,适合快速开发。

4.1 安装依赖
pip install yagmail
4.2 代码示例(支持纯文本、HTML、附件)
import yagmail

def send_email_with_yagmail(sender, auth_code, receivers, subject, content, attachments=None):
    """
    用yagmail简化发送邮件
    :param attachments: 附件路径列表(可选,如 ["file1.txt", "file2.xlsx"])
    """
    try:
        # 1. 初始化SMTP连接(host为SMTP服务器,如QQ邮箱为"smtp.qq.com")
        yag = yagmail.SMTP(user=sender, password=auth_code, host="smtp.qq.com")
        
        # 2. 发送邮件(contents支持字符串(纯文本)、HTML字符串、列表(正文+附件))
        if attachments:
            # 若有附件,contents格式为 [正文, 附件1路径, 附件2路径, ...]
            contents = [content] + attachments
        else:
            contents = content
        
        yag.send(
            to=receivers,       # 收件人(列表或单个邮箱字符串)
            subject=subject,    # 主题
            contents=contents   # 内容(正文+附件)
        )
        print("yagmail邮件发送成功!")
    except Exception as e:
        print(f"yagmail邮件发送失败:{e}")
    finally:
        # 3. 关闭连接
        yag.close()

# ------------------- 调用示例 -------------------
if __name__ == "__main__":
    # 发送带HTML和附件的邮件
    HTML_CONTENT = "<h3>yagmail测试</h3><p>这是用yagmail发送的HTML邮件,带附件。</p>"
    send_email_with_yagmail(
        sender="your_email@qq.com",
        auth_code="your_auth_code",
        receivers=["recipient1@163.com", "recipient2@gmail.com"],
        subject="yagmail简化邮件测试",
        content=HTML_CONTENT,
        attachments=["test_file.txt"]  # 可选附件
    )

四、关键注意事项

  1. SMTP服务与端口配置

    • 必须使用对应邮箱的SMTP服务器地址(如163邮箱为smtp.163.com),端口优先选择465(SSL加密,更安全),避免使用25端口(多数服务商已禁用非加密端口);
    • 若连接超时,检查网络是否屏蔽SMTP端口(如企业内网需开放465端口)。
  2. 授权码而非登录密码

    • 现代邮箱(如QQ、163)均不支持用登录密码直接登录SMTP服务,必须使用“授权码”;
    • 若出现smtplib.SMTPAuthenticationError: (535, b'Login Fail...'),检查授权码是否正确、是否过期(部分邮箱授权码长期有效,部分需定期重新生成)。
  3. 中文编码与乱码问题

    • 构造邮件时必须指定_charset="utf-8",避免中文主题或正文乱码;
    • 附件文件名含中文时,需用Header(filename, 'utf-8').encode()处理(如场景3示例)。
  4. 反垃圾邮件策略

    • 发件人邮箱与msg["From"]需一致(避免被判定为伪造邮件);
    • 邮件内容避免包含大量敏感词(如“垃圾邮件”“营销”“免费”)或链接(如短链接、可疑域名);
    • 批量发送时控制频率(如每分钟不超过10封),避免IP被邮箱服务商拉黑。
  5. 异常处理与日志

    • 捕获smtplib常见异常(如SMTPConnectError连接失败、SMTPTimeoutError超时、SMTPAuthenticationError授权失败);
    • 记录邮件发送日志(成功/失败、时间、收件人),便于问题排查。

五、总结

Python发送邮件的核心工具可分为两类:

  • 标准库(smtplib+email:灵活性高,支持纯文本、HTML、多附件等复杂场景,适合需要定制化的业务(如企业报表投递、系统告警),但代码量较多;
  • 第三方库(yagmail:API简洁,大幅简化代码,适合快速开发(如验证码、简单通知),但定制化能力弱于标准库。

实际开发中,需根据需求选择工具:简单场景用yagmail提升效率,复杂场景用标准库实现定制;同时需重点关注SMTP服务配置、授权码正确性、中文编码与反垃圾邮件策略,确保邮件稳定送达。通过本文的代码示例与注意事项,可覆盖绝大多数Python邮件发送需求,快速构建可靠的邮件发送功能。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐