Python smtplib模块详解:SMTP协议命令与SSL安全邮件发送实战

码头散步
码头散步
发布时间:2026-10-13| 4人浏览过
原创

本文深入解析Python标准库smtplib,详细讲解SMTP协议核心命令(HELO、MAIL FROM、RCPT TO等)及两种常见发信流程。重点介绍通过AUTH LOGIN进行身份认证的方法,以及利用SMTP_SSL类和starttls命令实现SSL安全邮件发送的两种技术方案,附带完整代码示例与注意事项。

Python内置的smtplib模块为发送电子邮件提供了便捷途径,它对复杂的SMTP协议进行了简洁封装,使得开发者能够轻松实现邮件发送功能。

SMTP协议包含一系列基本命令用于控制邮件传输过程:HELO用于向服务器标识用户身份;MAIL FROM初始化邮件传输并指定发件人;RCPT TO用于标识单个邮件接收者,通常跟随在MAIL命令之后,可指定多个接收者;DATA命令在确认所有接收者后启动数据传输,以单独一行的句点(.)作为结束标志;VRFY命令用于验证指定用户或邮箱是否存在,出于安全考虑,多数服务器已禁用此命令;EXPN命令用于验证邮箱列表或扩充邮箱列表,同样常被禁用;HELP命令用于查询服务器支持的命令集;NOOP命令执行无操作,服务器应响应OK;QUIT命令用于结束会话;RSET命令用于重置当前会话并取消正在进行的传输;此外还有MAIL FROM指定发送者地址和RCPT TO指定接收者地址的具体用法。

常规的SMTP会话主要分为两种方式。第一种是直接投递模式,例如向zzz@163.com发送邮件时,客户端直接连接163.com的邮件服务器并将信件投递给该用户。第二种是经过身份验证后的发信模式,例如用户希望通过自己在sina.com的邮箱向zzz@163.com发送邮件。此时,客户端首先连接sina.com的SMTP服务器,完成身份认证后,将邮件提交给sina.com服务器,由sina.com服务器负责将邮件最终投递到163.com。

第一种直接投递方式的命令执行流程大致如下:

1. 执行helo命令 2. 执行mail from命令指定发件人 3. 执行rcpt to命令指定收件人 4. 执行data命令开始发送数据 5. 执行quit命令结束会话

然而,第一种发送方式通常存在限制,即RCPT TO指定的邮件接收者必须在该服务器上真实存在,否则服务器将拒绝接收邮件。以下代码展示了这一过程:

 #-*- encoding: gb2312 -*-
import os, sys, string
import smtplib

# 邮件服务器地址
mailserver = "smtp.163.com"
# smtp会话过程中的mail from地址
from_addr = "asfgysg@zxsdf.com"
# smtp会话过程中的rcpt to地址
to_addr = "zhaoweikid@163.com"
# 信件内容
msg = "test mail"

svr = smtplib.SMTP(mailserver)
# 设置为调试模式,就是在会话过程中会有输出信息
svr.set_debuglevel(1)
# helo命令,docmd方法包括了获取对方服务器返回信息
svr.docmd("HELO server")
# mail from, 发送邮件发送者
svr.docmd("MAIL FROM: <%s>" % from_addr)
# rcpt to, 邮件接收者
svr.docmd("RCPT TO: <%s>" % to_addr)
# data命令,开始发送数据
svr.docmd("DATA")
# 发送正文数据
svr.send(msg)
# 比如以 . 作为正文发送结束的标记,用send发送的,所以要用getreply获取返回信息
svr.send(" . ")
svr.getreply()
# 发送结束,退出
svr.quit()

需要注意的是,163.com等主流邮箱服务商具备反垃圾邮件过滤机制,上述直接投递方法很可能无法通过反垃圾邮件系统的检测。因此,一般不建议个人用户采用这种方式发送邮件。

第二种发信方式流程有所不同,具体步骤如下: 1. 执行ehlo命令 2. 执行auth login进行身份认证 3. 执行mail from命令 4. 执行rcpt to命令 5. 执行data命令 6. 执行quit命令

相较于第一种方式,第二种方式增加了一个关键的认证环节,即通过auth login命令进行用户身份验证。

#-*- encoding: gb2312 -*-
import os, sys, string
import smtplib
import base64

# 邮件服务器地址
mailserver = "smtp.163.com"
# 邮件用户名
username = "xxxxxx@163.com"
# 密码
password = "xxxxxxx"
# smtp会话过程中的mail from地址
from_addr = "xxxxxx@163.com"
# smtp会话过程中的rcpt to地址
to_addr = "yyyyyy@163.com"
# 信件内容
msg = "my test mail"

svr = smtplib.SMTP(mailserver)
# 设置为调试模式,就是在会话过程中会有输出信息
svr.set_debuglevel(1)
# ehlo命令,docmd方法包括了获取对方服务器返回信息
svr.docmd("EHLO server")
# auth login 命令
svr.docmd("AUTH LOGIN")
# 发送用户名,是base64编码过的,用send发送的,所以要用getreply获取返回信息
svr.send(base64.encodestring(username))
svr.getreply()
# 发送密码
svr.send(base64.encodestring(password))
svr.getreply()
# mail from, 发送邮件发送者
svr.docmd("MAIL FROM: <%s>" % from_addr)
# rcpt to, 邮件接收者
svr.docmd("RCPT TO: <%s>" % to_addr)
# data命令,开始发送数据
svr.docmd("DATA")
# 发送正文数据
svr.send(msg)
# 比如以 . 作为正文发送结束的标记
svr.send(" . ")
svr.getreply()
# 发送结束,退出
svr.quit()

上述讨论的是普通邮件发送情况,但不可忽视的是,许多企业邮箱支持通过SSL加密发送安全邮件。实现SSL安全邮件主要有两种方案:一种是较老的做法,专门开启465端口来接收SSL邮件;另一种是更新的做法,在标准的25端口SMTP基础上增加starttls命令来支持安全传输。

下面介绍第一种方案的实现方法:

#-*- encoding: gb2312 -*-
import os, sys, string, socket
import smtplib


class SMTP_SSL (smtplib.SMTP):
  def __init__(self, host='', port=465, local_hostname=None, key=None, cert=None):
    self.cert = cert
    self.key = key
    smtplib.SMTP.__init__(self, host, port, local_hostname)
    
  def connect(self, host='localhost', port=465):
    if not port and (host.find(':') == host.rfind(':')):
      i = host.rfind(':')
      if i >= 0:
        host, port = host[:i], host[i+1:]
        try: port = int(port)
        except ValueError:
          raise socket.error, "nonnumeric port"
    if not port: port = 654
    if self.debuglevel > 0: print>>stderr, 'connect:', (host, port)
    msg = "getaddrinfo returns an empty list"
    self.sock = None
    for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
      af, socktype, proto, canonname, sa = res
      try:
        self.sock = socket.socket(af, socktype, proto)
        if self.debuglevel > 0: print>>stderr, 'connect:', (host, port)
        self.sock.connect(sa)
        # 新增加的创建ssl连接
        sslobj = socket.ssl(self.sock, self.key, self.cert)
      except socket.error, msg:
        if self.debuglevel > 0: 
          print>>stderr, 'connect fail:', (host, port)
        if self.sock:
          self.sock.close()
        self.sock = None
        continue
      break
    if not self.sock:
      raise socket.error, msg

    # 设置ssl
    self.sock = smtplib.SSLFakeSocket(self.sock, sslobj)
    self.file = smtplib.SSLFakeFile(sslobj);

    (code, msg) = self.getreply()
    if self.debuglevel > 0: print>>stderr, "connect:", msg
    return (code, msg)
    
if __name__ == '__main__':
  smtp = SMTP_SSL('192.168.2.10')
  smtp.set_debuglevel(1)
  smtp.sendmail("zzz@xxx.com", "zhaowei@zhaowei.com", "xxxxxxxxxxxxxxxxx")
  smtp.quit()

在此方案中,我从原有的smtplib.SMTP类派生出了新的SMTP_SSL类,专门用于处理SSL连接。测试中使用的192.168.2.10是我本地的测试服务器地址。

第二种方案则是新增starttls命令,这在smtplib库中已有对应方法smtplib.starttls()。需要注意的是,并非所有邮件系统都支持安全邮件功能,这需要从EHLO命令的返回值中确认,如果返回值中包含starttls关键字,则表明服务器支持该功能。相较于发送普通邮件的第二种方法,只需新增一行代码即可实现:

#-*- encoding: gb2312 -*-
import os, sys, string
import smtplib
import base64

# 邮件服务器地址
mailserver = "smtp.163.com"
# 邮件用户名
username = "xxxxxx@163.com"
# 密码
password = "xxxxxxx"
# smtp会话过程中的mail from地址
from_addr = "xxxxxx@163.com"
# smtp会话过程中的rcpt to地址
to_addr = "yyyyyy@163.com"
# 信件内容
msg = "my test mail"

svr = smtplib.SMTP(mailserver)
# 设置为调试模式,就是在会话过程中会有输出信息
svr.set_debuglevel(1)
# ehlo命令,docmd方法包括了获取对方服务器返回信息,如果支持安全邮件,返回值里会有starttls提示
svr.docmd("EHLO server")
svr.starttls() # <------ 这行就是新加的支持安全邮件的代码!
# auth login 命令
svr.docmd("AUTH LOGIN")
# 发送用户名,是base64编码过的,用send发送的,所以要用getreply获取返回信息
svr.send(base64.encodestring(username))
svr.getreply()
# 发送密码
svr.send(base64.encodestring(password))
svr.getreply()
# mail from, 发送邮件发送者
svr.docmd("MAIL FROM: <%s>" % from_addr)
# rcpt to, 邮件接收者
svr.docmd("RCPT TO: <%s>" % to_addr)
# data命令,开始发送数据
svr.docmd("DATA")
# 发送正文数据
svr.send(msg)
# 比如以 . 作为正文发送结束的标记
svr.send(" . ")
svr.getreply()
# 发送结束,退出
svr.quit()

请注意:为了简化代码展示,以上示例均未对服务器返回值进行判断。严格来说,应当检查返回的状态码,因为在SMTP协议中,只有返回代码为2xx或3xx时才能继续执行下一步操作,而返回4xx或5xx则表示出现错误。

以上就是本文的全部内容,希望对大家的学习有所帮助,也欢迎大家多多支持本站。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。