SMTP讲解 + Python 发送邮件案例

部署运行你感兴趣的模型镜像

SMTP(简单邮件传输协议)的端口和连接方式是邮件发送的核心技术细节,直接影响连接稳定性和安全性。不同邮箱服务商(如163、QQ、企业邮箱等)对端口和加密方式的支持存在差异,配置错误会导致“连接失败”“发送被拒”等问题。以下是详细讲解:

一、SMTP 核心端口及对应加密方式

SMTP 协议常用的端口有 25、465、587,其中后两者是现代邮箱推荐使用的安全端口,25端口因安全性问题逐渐被淘汰。

端口加密方式适用场景安全性兼容性(网络环境)
25通常不加密(明文)早期标准端口,部分邮箱(如企业自建邮箱)仍支持,但多数公共邮箱已禁用差(常被运营商/防火墙封锁)
465SSL 加密(隐式)连接建立时直接使用 SSL 加密通道,属于“传统安全端口”一般(部分网络可能封锁)
587STARTTLS(显式)先建立明文连接,再通过 STARTTLS 命令升级为加密通道,是现代推荐标准好(多数网络允许通行)

二、关键区别:465(SSL) vs 587(STARTTLS)

这两个端口是目前主流选择,但连接逻辑完全不同,配置错误会直接导致连接失败:

1. 465 端口(SSL 加密)
  • 连接流程:客户端与服务器建立连接时,直接使用 SSL 协议加密,全程无明文传输。
  • 代码实现:需使用 smtplib.SMTP_SSL() 类,且禁止调用 starttls() 方法(否则会导致加密冲突)。
    # 465端口正确写法
    with smtplib.SMTP_SSL(smtp_server, 465) as server:  # 直接用SSL连接
        server.login(sender, password)  # 登录(传输已加密)
        server.send_message(msg)
    
  • 适用场景:587端口被网络封锁时作为备选(如部分企业内网仅开放465)。
2. 587 端口(STARTTLS 加密)
  • 连接流程
    ① 先建立普通明文连接(非加密);
    ② 客户端发送 STARTTLS 命令,请求升级为加密通道;
    ③ 服务器同意后,后续所有数据(包括登录密码)均加密传输。
  • 代码实现:需使用 smtplib.SMTP() 类,且必须调用 starttls() 方法(否则会以明文传输密码,被服务器拒绝)。
    # 587端口正确写法
    with smtplib.SMTP(smtp_server, 587) as server:  # 先建立明文连接
        server.starttls()  # 关键:升级为加密通道(必须调用)
        server.login(sender, password)  # 登录(已加密)
        server.send_message(msg)
    
  • 适用场景:推荐优先使用,兼容性最好(多数邮箱服务商默认推荐)。

三、如何确定邮箱的正确端口和连接方式?

不同邮箱服务商的支持情况不同,需参考官方文档,以下是常见邮箱的配置:

邮箱类型推荐端口+加密方式SMTP服务器地址备注
163企业邮箱587 + STARTTLSsmtphz.qiye.163.com 或 smtp.qiye.163.com465 + SSL 作为备选
QQ邮箱587 + STARTTLSsmtp.qq.com465 + SSL 支持,但需用 SMTP_SSL 连接
163个人邮箱465 + SSLsmtp.163.com587 也支持,但官方文档优先推荐465
Gmail587 + STARTTLSsmtp.gmail.com465 支持,但需科学上网
企业自建邮箱25(需内部确认)通常为 smtp.企业域名(如 smtp.company.com)可能需要关闭加密(视内部配置而定)

四、常见错误及排查方法

1. 端口与加密方式不匹配(最常见)
  • 错误表现:连接超时、ssl.SSLError、服务器拒绝响应。
  • 例子
    • 用 465 端口却调用了 starttls()(加密冲突);
    • 用 587 端口却未调用 starttls()(服务器要求加密,拒绝明文登录)。
  • 排查:严格按照“端口-加密方式”对应关系修改代码(见上文代码示例)。
2. 端口被网络封锁
  • 错误表现ConnectionRefusedError(连接被拒绝)、TimeoutError(超时)。
  • 排查方法
    用命令行测试端口是否通畅(以 163企业邮箱为例):
    # Windows 用 telnet,Mac/Linux 用 nc
    telnet smtphz.qiye.163.com 587  # 测试587端口
    nc -zv smtphz.qiye.163.com 465  # 测试465端口
    
    • 若显示“连接失败”,说明端口被防火墙/运营商封锁,需切换端口或网络(如用手机热点)。
3. 服务器不支持该端口
  • 错误表现:连接后服务器无响应,或返回 554 Denied(拒绝连接)。
  • 排查:确认邮箱服务商是否支持该端口(参考官方文档),例如部分企业邮箱仅开放25端口。

五、最佳实践

  1. 优先使用 587 + STARTTLS:兼容性最好,被封锁概率低,符合现代安全标准。
  2. 代码中明确区分端口逻辑:通过条件判断自动适配不同端口的连接方式(如上文163企业邮箱代码)。
  3. 测试时开启 debug 模式:通过 server.set_debuglevel(1) 打印连接日志,查看服务器响应,快速定位端口/加密问题。
  4. 避免使用 25 端口:多数公共邮箱已禁用,且明文传输存在密码泄露风险。

六、完整代码(Python3)

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

def send_163_enterprise_email(smtp_server, smtp_port, sender_email, sender_auth, 
                              receiver_email, subject, message, attachments=None):
    """
    适配163企业邮箱的邮件发送函数
    :param smtp_server: 163企业邮箱SMTP服务器(如smtphz.qiye.163.com)
    :param smtp_port: SMTP端口(587或465)
    :param sender_email: 发件人企业邮箱(如admin@company.com)
    :param sender_auth: 发件人授权(企业邮箱密码 或 独立授权码)
    :param receiver_email: 收件人邮箱(单个字符串或多个列表)
    :param subject: 邮件主题(自动处理UTF-8编码)
    :param message: 邮件正文(纯文本)
    :param attachments: 附件路径列表(可选)
    :return: 发送状态(True=成功,False=失败)
    """
    # 1. 创建邮件容器(支持附件)
    msg = MIMEMultipart()
    msg['From'] = sender_email  # 发件人(必须与企业邮箱账号一致)
    # 处理多个收件人(格式:用逗号连接)
    msg['To'] = receiver_email if isinstance(receiver_email, str) else ', '.join(receiver_email)
    # 主题编码(避免中文乱码)
    msg['Subject'] = MIMEText(subject, 'plain', 'utf-8')['Subject']

    # 2. 添加纯文本正文(UTF-8编码,避免中文乱码)
    msg.attach(MIMEText(message, 'plain', 'utf-8'))

    # 3. 添加附件(若有)
    if attachments and isinstance(attachments, list):
        for file_path in attachments:
            if not os.path.exists(file_path):
                print(f"警告:附件不存在 - {file_path}")
                continue
            # 读取附件文件
            with open(file_path, 'rb') as f:
                part = MIMEBase('application', 'octet-stream')
                part.set_payload(f.read())
            # Base64编码(确保附件可正常传输)
            encoders.encode_base64(part)
            # 设置附件头(显示文件名,处理中文文件名)
            filename = os.path.basename(file_path)
            part.add_header(
                'Content-Disposition',
                f'attachment; filename={MIMEText(filename, "utf-8").as_string()}'
            )
            msg.attach(part)

    # 4. 连接SMTP服务器并发送
    try:
        # 根据端口选择连接方式(587=SMTP+STARTTLS,465=SMTP_SSL)
        if smtp_port == 465:
            # 465端口:直接SSL加密连接(无需starttls())
            with smtplib.SMTP_SSL(smtp_server, smtp_port, timeout=10) as server:
                server.login(sender_email, sender_auth)  # 登录企业邮箱
                server.send_message(msg)                 # 发送邮件
        else:
            # 587端口:明文连接后升级TLS(推荐)
            with smtplib.SMTP(smtp_server, smtp_port, timeout=10) as server:
                server.starttls()                       # 开启TLS加密
                server.login(sender_email, sender_auth)  # 登录企业邮箱
                server.send_message(msg)                 # 发送邮件

        print("✅ 163企业邮箱邮件发送成功(服务器已接收)")
        return True

    except Exception as e:
        # 若出现QUIT阶段的异常(如之前的b'\x00\x00\x00'),先检查收件箱
        if "b'OK: queued as'" in str(e) or "250 OK" in str(e):
            print("✅ 邮件已提交到服务器(忽略末端连接异常),请查收件箱")
            return True
        else:
            print(f"❌ 邮件发送失败:{str(e)}")
            return False

# -------------------------- 配置参数(替换为你的企业邮箱信息) --------------------------
if __name__ == "__main__":
    # 1. 163企业邮箱SMTP配置(固定)
    SMTP_SERVER = "smtphz.qiye.163.com"  # 杭州节点,其他节点可替换为smtp.qiye.163.com
    SMTP_PORT = 587                      # 推荐587(兼容性强),若失败换465

    # 2. 发件人信息(必须是企业邮箱分配的账号)
    SENDER_EMAIL = "your-account@your-company.com"  # 替换为你的企业邮箱(如admin@xxx.com)
    SENDER_AUTH = "your-password-or-auth-code"      # 替换为:企业邮箱密码 或 独立授权码

    # 3. 收件人信息(可单个或多个)
    RECEIVER_EMAIL = [
        "receiver1@example.com", 
        "receiver2@your-company.com"
    ]  # 替换为实际收件人邮箱

    # 4. 邮件内容
    EMAIL_SUBJECT = "163企业邮箱测试邮件"  # 邮件主题(中文可直接写)
    EMAIL_MESSAGE = "这是用Python适配163企业邮箱发送的测试邮件,包含正文和附件(可选)。"  # 正文

    # 5. 附件(可选,无附件则设为None)
    EMAIL_ATTACHMENTS = [
        "test.txt",       # 替换为你的附件路径
        "report.pdf"
    ]
    # EMAIL_ATTACHMENTS = None  # 无附件时启用这行

    # 6. 调用函数发送
    send_163_enterprise_email(
        smtp_server=SMTP_SERVER,
        smtp_port=SMTP_PORT,
        sender_email=SENDER_EMAIL,
        sender_auth=SENDER_AUTH,
        receiver_email=RECEIVER_EMAIL,
        subject=EMAIL_SUBJECT,
        message=EMAIL_MESSAGE,
        attachments=EMAIL_ATTACHMENTS
    )

掌握端口与加密方式的对应关系,是解决“邮件发送失败”问题的核心技能,配置时务必严格匹配邮箱服务商的要求。

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值