smtplib — SMTP 协议客户端

源代码: Lib/smtplib.py


smtplib 模块定义了一个 SMTP 客户端会话对象,该对象可用于向任何具有 SMTP 或 ESMTP 监听守护程序的 Internet 计算机发送邮件。 有关 SMTP 和 ESMTP 操作的详细信息,请参考 RFC 821 (简单邮件传输协议) 和 RFC 1869 (SMTP 服务扩展)。

可用性: 不支持 WASI。

此模块在 WebAssembly 上不起作用或不可用。 有关更多信息,请参阅 WebAssembly 平台

class smtplib.SMTP(host='', port=0, local_hostname=None, [timeout, ]source_address=None)

SMTP 实例封装了一个 SMTP 连接。它具有支持完整 SMTP 和 ESMTP 操作的方法。如果给出了可选的 hostport 参数,则在初始化期间使用这些参数调用 SMTP connect() 方法。 如果指定了 local_hostname,则在 HELO/EHLO 命令中用作本地主机的 FQDN。 否则,使用 socket.getfqdn() 查找本地主机名。 如果 connect() 调用返回除成功代码之外的任何内容,则会引发 SMTPConnectError。 可选的 timeout 参数指定阻塞操作(如连接尝试)的超时时间(以秒为单位)(如果未指定,则将使用全局默认超时设置)。如果超时到期,则会引发 TimeoutError。 可选的 source_address 参数允许在具有多个网络接口的计算机中绑定到某个特定的源地址,和/或绑定到某个特定的源 TCP 端口。它采用 2 元组 (host, port),以便套接字在连接之前绑定到其作为源地址。 如果省略(或者如果 hostport 分别是 '' 和/或 0),将使用操作系统默认行为。

在正常使用中,您应该只需要初始化/连接、sendmail()SMTP.quit() 方法。 下面包含一个示例。

SMTP 类支持 with 语句。 像这样使用时,当 with 语句退出时,会自动发出 SMTP QUIT 命令。 例如:

>>> from smtplib import SMTP
>>> with SMTP("domain.org") as smtp:
...     smtp.noop()
...
(250, b'Ok')
>>>

所有命令都会引发 审计事件 smtplib.SMTP.send,其参数为 selfdata,其中 data 是即将发送到远程主机的字节。

在 3.3 版本中更改: 添加了对 with 语句的支持。

在 3.3 版本中更改: 添加了 source_address 参数。

3.5 版本新增: 现在支持 SMTPUTF8 扩展 (RFC 6531)。

在 3.9 版本中更改: 如果 timeout 参数设置为零,则会引发 ValueError,以防止创建非阻塞套接字。

class smtplib.SMTP_SSL(host='', port=0, local_hostname=None, *, [timeout, ]context=None, source_address=None)

SMTP_SSL 实例的行为与 SMTP 实例完全相同。 SMTP_SSL 应该用于从连接开始就需要 SSL 并且不适合使用 starttls() 的情况。 如果未指定 host,则使用本地主机。 如果 port 为零,则使用标准的 SMTP over SSL 端口 (465)。可选参数 local_hostnametimeoutsource_address 与在 SMTP 类中的含义相同。 context 也是可选的,可以包含一个 SSLContext,并允许配置安全连接的各个方面。 请阅读 安全注意事项 以了解最佳实践。

在 3.3 版本中更改: 添加了 context

在 3.3 版本中更改: 添加了 source_address 参数。

在 3.4 版本中更改: 该类现在支持使用 ssl.SSLContext.check_hostname服务器名称指示的主机名检查(请参阅 ssl.HAS_SNI)。

在 3.9 版本中更改: 如果 timeout 参数设置为零,则会引发 ValueError,以防止创建非阻塞套接字。

在 3.12 版本中更改: 已删除已弃用的 keyfilecertfile 参数。

class smtplib.LMTP(host='', port=LMTP_PORT, local_hostname=None, source_address=None[, timeout])

LMTP 协议与 ESMTP 非常相似,它很大程度上基于标准的 SMTP 客户端。LMTP 通常使用 Unix 套接字,因此我们的 connect() 方法必须同时支持 Unix 套接字和常规的 host:port 服务器。可选参数 local_hostnamesource_addressSMTP 类中的含义相同。要指定 Unix 套接字,您必须使用 host 的绝对路径,以 '/' 开头。

支持使用常规 SMTP 机制进行身份验证。当使用 Unix 套接字时,LMTP 通常不支持或不需要任何身份验证,但具体情况可能会有所不同。

在 3.9 版本中更改: 添加了可选的 timeout 参数。

还定义了一系列很好的异常。

exception smtplib.SMTPException

OSError 的子类,是此模块提供的所有其他异常的基类。

在 3.4 版本中更改: SMTPException 成为 OSError 的子类

exception smtplib.SMTPServerDisconnected

当服务器意外断开连接,或尝试在连接到服务器之前使用 SMTP 实例时,将引发此异常。

exception smtplib.SMTPResponseException

所有包含 SMTP 错误代码的异常的基类。当 SMTP 服务器返回错误代码时,会在某些情况下生成这些异常。错误代码存储在错误的 smtp_code 属性中,并且 smtp_error 属性设置为错误消息。

exception smtplib.SMTPSenderRefused

发件人地址被拒绝。除了在所有 SMTPResponseException 异常上设置的属性之外,此异常还将 'sender' 设置为 SMTP 服务器拒绝的字符串。

exception smtplib.SMTPRecipientsRefused

所有收件人地址都被拒绝。每个收件人的错误都可以通过属性 recipients 访问,它是一个与 SMTP.sendmail() 返回的完全相同的字典。

exception smtplib.SMTPDataError

SMTP 服务器拒绝接受消息数据。

exception smtplib.SMTPConnectError

在与服务器建立连接期间发生错误。

exception smtplib.SMTPHeloError

服务器拒绝了我们的 HELO 消息。

exception smtplib.SMTPNotSupportedError

服务器不支持尝试的命令或选项。

在 3.5 版本中添加。

exception smtplib.SMTPAuthenticationError

SMTP 身份验证出错。很可能是服务器不接受提供的用户名/密码组合。

另请参阅

RFC 821 - 简单邮件传输协议

SMTP 的协议定义。本文档涵盖了 SMTP 的模型、操作规程和协议详细信息。

RFC 1869 - SMTP 服务扩展

SMTP 的 ESMTP 扩展的定义。这描述了一个使用新命令扩展 SMTP 的框架,支持动态发现服务器提供的命令,并定义了一些其他命令。

SMTP 对象

一个 SMTP 实例具有以下方法

SMTP.set_debuglevel(level)

设置调试输出级别。level 的值为 1 或 True 会导致为连接以及发送到服务器和从服务器接收的所有消息输出调试消息。level 的值为 2 会导致这些消息带有时间戳。

在 3.5 版本中更改: 添加了调试级别 2。

SMTP.docmd(cmd, args='')

向服务器发送命令 cmd。可选参数 args 只是简单地与命令连接,用空格分隔。

这将返回一个由数字响应代码和实际响应行组成的 2 元组(多行响应连接成一行。)

在正常操作中,不需要显式调用此方法。它用于实现其他方法,并且可能对测试私有扩展有用。

如果在等待回复时与服务器的连接丢失,将引发 SMTPServerDisconnected

SMTP.connect(host='localhost', port=0)

连接到给定端口上的主机。默认值是连接到标准 SMTP 端口 (25) 上的本地主机。如果主机名以冒号 (':') 后跟一个数字结尾,则该后缀将被剥离,并且该数字将被解释为要使用的端口号。如果在实例化期间指定了主机,则此方法会自动由构造函数调用。返回服务器在其连接响应中发送的响应代码和消息的 2 元组。

引发一个 审计事件 smtplib.connect,其中包含参数 selfhostport

SMTP.helo(name='')

使用 HELO 向 SMTP 服务器标识自己。主机名参数默认为本地主机的完全限定域名。服务器返回的消息存储为对象的 helo_resp 属性。

在正常操作中,不需要显式调用此方法。必要时,sendmail() 会隐式调用它。

SMTP.ehlo(name='')

使用 EHLO 向 ESMTP 服务器标识自己。主机名参数默认为本地主机的完全限定域名。检查响应中的 ESMTP 选项,并将其存储以供 has_extn() 使用。还设置几个信息性属性:服务器返回的消息存储为 ehlo_resp 属性,does_esmtp 根据服务器是否支持 ESMTP 设置为 TrueFalse,并且 esmtp_features 将是一个字典,其中包含此服务器支持的 SMTP 服务扩展的名称及其参数(如果有)。

除非你希望在发送邮件前使用 has_extn(),否则通常不需要显式调用此方法。 当必要时,sendmail() 会隐式调用它。

SMTP.ehlo_or_helo_if_needed()

如果当前会话之前没有发送过 EHLOHELO 命令,此方法将调用 ehlo() 和/或 helo()。 它会首先尝试 ESMTP EHLO

SMTPHeloError

服务器没有正确响应 HELO 问候语。

SMTP.has_extn(name)

如果 name 在服务器返回的 SMTP 服务扩展集合中,则返回 True,否则返回 False。 忽略大小写。

SMTP.verify(address)

使用 SMTP VRFY 检查此服务器上地址的有效性。 如果用户地址有效,则返回包含代码 250 和完整的 RFC 822 地址(包括人名)的元组。 否则,返回 400 或更大的 SMTP 错误代码和一个错误字符串。

注意

许多站点为了阻止垃圾邮件发送者而禁用 SMTP VRFY

SMTP.login(user, password, *, initial_response_ok=True)

在需要身份验证的 SMTP 服务器上登录。 参数是用于身份验证的用户名和密码。 如果当前会话之前没有发送过 EHLOHELO 命令,此方法将首先尝试 ESMTP EHLO。 如果身份验证成功,此方法将正常返回,否则可能会引发以下异常

SMTPHeloError

服务器没有正确响应 HELO 问候语。

SMTPAuthenticationError

服务器不接受用户名/密码组合。

SMTPNotSupportedError

服务器不支持 AUTH 命令。

SMTPException

未找到合适的身份验证方法。

如果服务器声明支持 smtplib 支持的每种身份验证方法,则会依次尝试这些方法。 有关支持的身份验证方法的列表,请参见 auth()initial_response_ok 将传递给 auth()

可选关键字参数 initial_response_ok 指定对于支持它的身份验证方法,是否可以按照 RFC 4954 中的规定,将“初始响应”与 AUTH 命令一起发送,而不是需要质询/响应。

在 3.5 版本中更改: 可能会引发 SMTPNotSupportedError 异常,并添加了 initial_response_ok 参数。

SMTP.auth(mechanism, authobject, *, initial_response_ok=True)

为指定的身份验证 mechanism 发出 SMTP AUTH 命令,并通过 authobject 处理质询响应。

mechanism 指定要用作 AUTH 命令参数的身份验证机制;有效值是 esmtp_featuresauth 元素中列出的值。

authobject 必须是可调用的对象,它接受一个可选的单个参数。

data = authobject(challenge=None)

如果可选关键字参数 initial_response_ok 为 true,则将首先调用不带参数的 authobject()。 它可以返回 RFC 4954 中的“初始响应” ASCII str,该字符串将被编码并与 AUTH 命令一起发送,如下所示。 如果 authobject() 不支持初始响应(例如,因为它需要质询),则当使用 challenge=None 调用时,它应返回 None。 如果 initial_response_ok 为 false,则不会首先使用 None 调用 authobject()

如果初始响应检查返回 None,或者如果 initial_response_ok 为 false,则将调用 authobject() 来处理服务器的质询响应;传递给它的 challenge 参数将是一个 bytes。 它应该返回 ASCII str data,该 data 将进行 base64 编码并发送到服务器。

SMTP 类为 CRAM-MD5PLAINLOGIN 机制提供了 authobjects;它们分别命名为 SMTP.auth_cram_md5SMTP.auth_plainSMTP.auth_login。 它们都要求将 SMTP 实例的 userpassword 属性设置为适当的值。

用户代码通常不需要直接调用 auth,而是可以调用 login() 方法,该方法将依次尝试上述每种机制,按列出的顺序排列。auth 的公开是为了方便实现 smtplib 未直接(或尚未)支持的身份验证方法。

在 3.5 版本中添加。

SMTP.starttls(*, context=None)

将 SMTP 连接置于 TLS(传输层安全性)模式。 之后的所有 SMTP 命令都将被加密。 然后,您应再次调用 ehlo()

如果提供了 keyfilecertfile,则它们用于创建 ssl.SSLContext

可选的 context 参数是一个 ssl.SSLContext 对象; 这是使用密钥文件和证书文件的替代方法,如果指定了该对象,则 keyfilecertfile 都应为 None

如果当前会话之前没有发送过 EHLOHELO 命令,此方法将首先尝试 ESMTP EHLO

在 3.12 版本中更改: 已删除已弃用的 keyfilecertfile 参数。

SMTPHeloError

服务器没有正确响应 HELO 问候语。

SMTPNotSupportedError

服务器不支持 STARTTLS 扩展。

RuntimeError

您的 Python 解释器无法使用 SSL/TLS 支持。

在 3.3 版本中更改: 添加了 context

3.4 版本更改: 该方法现在支持使用 SSLContext.check_hostname 进行主机名检查和服务器名称指示(请参阅 HAS_SNI)。

3.5 版本更改: 现在,对于缺少 STARTTLS 支持而引发的错误,将使用 SMTPNotSupportedError 子类,而不是基类 SMTPException

SMTP.sendmail(from_addr, to_addrs, msg, mail_options=(), rcpt_options=())

发送邮件。必需的参数是 RFC 822 发件人地址字符串,一个 RFC 822 收件人地址字符串列表(一个单独的字符串将被视为包含 1 个地址的列表)和一个消息字符串。调用者可以传递一个 ESMTP 选项列表(例如 8bitmime),以便在 MAIL FROM 命令中作为 mail_options 使用。应该与所有 RCPT 命令一起使用的 ESMTP 选项(例如 DSN 命令)可以作为 rcpt_options 传递。(如果您需要对不同的收件人使用不同的 ESMTP 选项,则必须使用低级方法(例如 mail()rcpt()data())来发送消息。)

注意

from_addrto_addrs 参数用于构造传输代理使用的消息信封。sendmail 不会以任何方式修改消息头。

msg 可以是包含 ASCII 范围字符的字符串或字节字符串。字符串使用 ascii 编解码器编码为字节,并且单独的 \r\n 字符会转换为 \r\n 字符。字节字符串不会被修改。

如果此会话中之前没有 EHLOHELO 命令,则此方法会首先尝试 ESMTP EHLO。如果服务器执行 ESMTP,则消息大小和每个指定的选项都将传递给它(如果该选项在服务器通告的功能集中)。如果 EHLO 失败,则将尝试 HELO 并取消 ESMTP 选项。

如果邮件至少被一个收件人接受,此方法将正常返回。否则,它会引发异常。也就是说,如果此方法没有引发异常,则应该有人收到您的邮件。如果此方法没有引发异常,则它会返回一个字典,其中包含每个被拒绝的收件人的一个条目。每个条目包含一个由 SMTP 错误代码和服务器发送的随附错误消息组成的元组。

如果 mail_options 中包含 SMTPUTF8,并且服务器支持它,则 from_addrto_addrs 可以包含非 ASCII 字符。

此方法可能会引发以下异常

SMTPRecipientsRefused

所有收件人都被拒绝。没有人收到邮件。异常对象的 recipients 属性是一个字典,其中包含有关被拒绝的收件人的信息(类似于至少一个收件人被接受时返回的信息)。

SMTPHeloError

服务器没有正确响应 HELO 问候语。

SMTPSenderRefused

服务器不接受 from_addr

SMTPDataError

服务器回复了一个意外的错误代码(而不是拒绝收件人)。

SMTPNotSupportedError

mail_options 中给出了 SMTPUTF8,但服务器不支持。

除非另有说明,否则即使在引发异常后,连接也会保持打开状态。

3.2 版本更改: msg 可以是字节字符串。

3.5 版本更改: 添加了 SMTPUTF8 支持,如果指定了 SMTPUTF8 但服务器不支持,则可能会引发 SMTPNotSupportedError

SMTP.send_message(msg, from_addr=None, to_addrs=None, mail_options=(), rcpt_options=())

这是一种方便的方法,用于调用 sendmail(),其中消息由 email.message.Message 对象表示。参数的含义与 sendmail() 相同,只是 msg 是一个 Message 对象。

如果 from_addrNoneto_addrsNone,则 send_message 会使用从 msg 的头中提取的地址来填充这些参数,如 RFC 5322 中所指定的:如果存在 Sender 字段,则 from_addr 设置为该字段,否则设置为 From 字段。to_addrs 组合了 msgToCcBcc 字段的值(如果有)。如果消息中恰好出现一组 Resent-* 标头,则会忽略常规标头,并改用 Resent-* 标头。如果消息包含多组 Resent-* 标头,则会引发 ValueError,因为无法明确检测到最新的一组 Resent- 标头。

send_message 使用 BytesGeneratormsg 序列化,并将 \r\n 作为 linesep,并调用 sendmail() 来传输生成的消息。无论 from_addrto_addrs 的值如何,send_message 都不会传输可能出现在 msg 中的任何 BccResent-Bcc 标头。如果 from_addrto_addrs 中的任何地址包含非 ASCII 字符,并且服务器不通告 SMTPUTF8 支持,则会引发 SMTPNotSupported 错误。否则,将使用其 policy 的克隆(其中 utf8 属性设置为 True)来序列化 Message,并将 SMTPUTF8BODY=8BITMIME 添加到 mail_options

3.2 版本中新增。

3.5 版本新增: 对国际化地址的支持 (SMTPUTF8)。

SMTP.quit()

终止 SMTP 会话并关闭连接。返回 SMTP QUIT 命令的结果。

还支持与标准 SMTP/ESMTP 命令 HELPRSETNOOPMAILRCPTDATA 相对应的低级方法。通常不需要直接调用这些方法,因此此处不作介绍。有关详细信息,请查阅模块代码。

SMTP 示例

此示例提示用户输入消息信封中需要的地址(“To”和“From”地址)以及要传递的消息。请注意,要包含在消息中的标头必须包含在输入的消息中;此示例不对 RFC 822 标头进行任何处理。特别是,“To”和“From”地址必须明确包含在消息头中

import smtplib

def prompt(title):
    return input(title).strip()

from_addr = prompt("From: ")
to_addrs  = prompt("To: ").split()
print("Enter message, end with ^D (Unix) or ^Z (Windows):")

# Add the From: and To: headers at the start!
lines = [f"From: {from_addr}", f"To: {', '.join(to_addrs)}", ""]
while True:
    try:
        line = input()
    except EOFError:
        break
    else:
        lines.append(line)

msg = "\r\n".join(lines)
print("Message length is", len(msg))

server = smtplib.SMTP("localhost")
server.set_debuglevel(1)
server.sendmail(from_addr, to_addrs, msg)
server.quit()

注意

通常,您需要使用 email 包的功能来构造电子邮件消息,然后可以通过 send_message() 发送该消息;请参阅 email: 示例