21.17. smtplib — SMTP 协议客户端 — Python 文档
21.17. smtplib — SMTP 协议客户端
smtplib 模块定义了一个 SMTP 客户端会话对象,该对象可用于将邮件发送到任何带有 SMTP 或 ESMTP 侦听器守护程序的 Internet 机器。 有关 SMTP 和 ESMTP 操作的详细信息,请参阅 RFC 821(简单邮件传输协议)和 RFC 1869(SMTP 服务扩展)。
- class smtplib.SMTP(host=, port=0, local_hostname=None, [timeout, ]source_address=None)
SMTP 实例封装了一个 SMTP 连接。 它的方法支持完整的 SMTP 和 ESMTP 操作。 如果给出了可选的主机和端口参数,则在初始化期间使用这些参数调用 SMTP connect() 方法。 如果指定,local_hostname 用作 HELO/EHLO 命令中本地主机的 FQDN。 否则,使用 socket.getfqdn() 找到本地主机名。 如果 connect() 调用返回成功代码以外的任何内容,则会引发 SMTPConnectError。 可选的 timeout 参数指定阻塞操作(如连接尝试)的超时(以秒为单位)(如果未指定,将使用全局默认超时设置)。 如果超时到期,则会引发 socket.timeout。 可选的 source_address 参数允许绑定到具有多个网络接口的机器中的某个特定源地址,和/或某个特定源 TCP 端口。 它需要一个 2 元组(主机,端口),套接字在连接之前绑定到作为其源地址。 如果省略(或者如果主机或端口分别为
和/或 0),将使用操作系统默认行为。
对于正常使用,您应该只需要初始化/连接、sendmail() 和 SMTP.quit() 方法。 下面包含一个示例。
SMTP 类支持 [X37X]with 语句。 像这样使用时,当with语句退出时自动发出SMTP
QUIT
命令。 例如:>>> from smtplib import SMTP >>> with SMTP("domain.org") as smtp: ... smtp.noop() ... (250, b'Ok') >>>
在 3.3 版更改:添加了对 with 语句的支持。
3.3 版更改:添加了 source_address 参数。
3.5 版新功能:现在支持 SMTPUTF8 扩展(RFC 6531)。
- class smtplib.SMTP_SSL(host=, port=0, local_hostname=None, keyfile=None, certfile=None, [timeout, ]context=None, source_address=None)
SMTP_SSL 实例的行为与 SMTP 的实例完全相同。 SMTP_SSL 应该用于从连接开始就需要 SSL 并且使用
starttls()
不合适的情况。 如果未指定 host,则使用本地主机。 如果 port 为零,则使用标准的 SMTP-over-SSL 端口 (465)。 可选参数 local_hostname、timeout 和 source_address 与它们在 SMTP 类中的含义相同。 context 也是可选的,可以包含 SSLContext 并允许配置安全连接的各个方面。 请阅读 安全注意事项 以获得最佳实践。keyfile 和 certfile 是 context 的传统替代品,可以指向用于 SSL 连接的 PEM 格式的私钥和证书链文件。
3.3 版更改:添加了 上下文 。
3.3 版更改:添加了 source_address 参数。
3.4 版更改: 该类现在支持使用 ssl.SSLContext.check_hostname 和 服务器名称指示 进行主机名检查(请参阅 ssl.HAS_SNI )。
自 3.6 版起已弃用:keyfile 和 certfile 已弃用,取而代之的是 context。 请改用 ssl.SSLContext.load_cert_chain(),或让 ssl.create_default_context() 为您选择系统的可信 CA 证书。
- class smtplib.LMTP(host=, port=LMTP_PORT, local_hostname=None, source_address=None)
LMTP 协议与 ESMTP 非常相似,很大程度上基于标准 SMTP 客户端。 为 LMTP 使用 Unix 套接字是很常见的,所以我们的
connect()
方法必须支持它以及常规的主机:端口服务器。 可选参数 local_hostname 和 source_address 的含义与它们在 SMTP 类中的含义相同。 要指定 Unix 套接字,您必须对 host 使用绝对路径,以“/”开头。支持使用常规 SMTP 机制进行身份验证。 使用 Unix 套接字时,LMTP 通常不支持或不需要任何身份验证,但您的里程可能会有所不同。
还定义了一个不错的例外选择:
- 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 的框架,支持动态发现服务器提供的命令,并定义了一些额外的命令。
21.17.1. 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 元组。
- SMTP.helo(name=)
使用
HELO
向 SMTP 服务器表明您自己。 hostname 参数默认为本地主机的完全限定域名。 服务器返回的消息存储为对象的helo_resp
属性。在正常操作中,不需要显式调用此方法。 必要时,它会被 sendmail() 隐式调用。
- SMTP.ehlo(name=)
使用
EHLO
向 ESMTP 服务器表明您自己。 hostname 参数默认为本地主机的完全限定域名。 检查 ESMTP 选项的响应并存储它们以供 has_extn() 使用。 还设置了几个信息属性:服务器返回的消息存储为ehlo_resp
属性,does_esmtp
根据服务器是否支持ESMTP设置为true或false,以及esmtp_features
] 将是一个字典,其中包含此服务器支持的 SMTP 服务扩展名及其参数(如果有)。除非您希望在发送邮件之前使用 has_extn(),则不需要显式调用此方法。 必要时,它会被 sendmail() 隐式调用。
SMTPHeloError
- 服务器没有正确回复
HELO
问候语。
- SMTP.verify(address)
使用 SMTP
VRFY
检查此服务器上地址的有效性。 如果用户地址有效,则返回由代码 250 和完整的 RFC 822 地址(包括人名)组成的元组。 否则返回 400 或更大的 SMTP 错误代码和错误字符串。笔记
许多站点禁用 SMTP
VRFY
以阻止垃圾邮件发送者。
- SMTP.login(user, password, *, initial_response_ok=True)
登录需要身份验证的 SMTP 服务器。 参数是要进行身份验证的用户名和密码。 如果之前没有
EHLO
或HELO
命令此会话,则此方法首先尝试 ESMTPEHLO
。 如果身份验证成功,此方法将正常返回,或者可能引发以下异常: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)
为指定的身份验证 机制 发出
SMTP
AUTH
命令,并通过 authobject 处理质询响应。mechanism 指定使用哪种身份验证机制作为
AUTH
命令的参数; 有效值是在esmtp_features
的auth
元素中列出的值。authobject 必须是一个带有可选单个参数的可调用对象:
数据 = authobject(挑战=无)
如果可选关键字参数 initial_response_ok 为真,则
authobject()
将首先被调用而没有参数。 它可以返回 RFC 4954“初始响应”ASCIIstr
,它将被编码并与AUTH
命令一起发送,如下所示。 如果authobject()
不支持初始响应(例如 因为它需要一个挑战),当用challenge=None
调用时它应该返回None
。 如果 initial_response_ok 为 false,则authobject()
将不会首先与None
一起调用。如果初始响应检查返回
None
,或者initial_response_ok为false,则调用authobject()
处理服务器的challenge响应; 它传递的 challenge 参数将是bytes
。 它应该返回 ASCIIstr
data,它将被 base64 编码并发送到服务器。SMTP
类为CRAM-MD5
、PLAIN
和LOGIN
机制提供authobjects
; 它们分别命名为SMTP.auth_cram_md5
、SMTP.auth_plain
和SMTP.auth_login
。 它们都要求将SMTP
实例的user
和password
属性设置为适当的值。用户代码通常不需要直接调用
auth
,而是可以调用 login() 方法,该方法将按列出的顺序依次尝试上述每种机制。auth
是为了促进 smtplib 不(或尚未)直接支持的身份验证方法的实现。3.5 版中的新功能。
- SMTP.starttls(keyfile=None, certfile=None, context=None)
将 SMTP 连接置于 TLS(传输层安全)模式。 随后的所有 SMTP 命令都将被加密。 然后您应该再次调用 ehlo()。
如果提供了 keyfile 和 certfile,它们将用于创建 ssl.SSLContext。
可选的 context 参数是一个 ssl.SSLContext 对象; 这是使用密钥文件和证书文件的替代方法,如果指定 keyfile 和 certfile 应该是
None
。如果之前没有
EHLO
或HELO
命令此会话,则此方法首先尝试 ESMTPEHLO
。自 3.6 版起已弃用:keyfile 和 certfile 已弃用,取而代之的是 context。 请改用 ssl.SSLContext.load_cert_chain(),或让 ssl.create_default_context() 为您选择系统的可信 CA 证书。
SMTPHeloError
服务器没有正确回复
HELO
问候语。SMTPNotSupportedError
服务器不支持 STARTTLS 扩展。
RuntimeError
您的 Python 解释器不支持 SSL/TLS。
3.3 版更改:添加了 上下文 。
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 个地址的列表)和消息字符串。 调用者可以将要在
MAIL FROM
命令中使用的 ESMTP 选项列表(例如8bitmime
)作为 mail_options 传递。 应该与所有RCPT
命令一起使用的 ESMTP 选项(例如DSN
命令)可以作为 rcpt_options 传递。 (如果您需要对不同的收件人使用不同的 ESMTP 选项,则必须使用mail()
、rcpt()
和data()
等低级方法来发送邮件。)笔记
from_addr 和 to_addrs 参数用于构造传输代理使用的消息信封。
sendmail
不会以任何方式修改消息头。msg 可以是包含 ASCII 范围内字符的字符串,也可以是字节字符串。 使用 ascii 编解码器将字符串编码为字节,并将单独的
\r
和\n
字符转换为\r\n
字符。 不修改字节字符串。如果之前没有
EHLO
或HELO
命令此会话,则此方法首先尝试 ESMTPEHLO
。 如果服务器执行 ESMTP,消息大小和每个指定的选项将传递给它(如果该选项在服务器通告的功能集中)。 如果EHLO
失败,将尝试HELO
并抑制 ESMTP 选项。如果至少有一个收件人接受了邮件,则此方法将正常返回。 否则会引发异常。 也就是说,如果此方法不引发异常,那么应该有人收到您的邮件。 如果此方法未引发异常,则它返回一个字典,其中每个被拒绝的收件人都有一个条目。 每个条目都包含一个 SMTP 错误代码元组和服务器发送的伴随错误消息。
如果
SMTPUTF8
包含在 mail_options 中,并且服务器支持它,则 from_addr 和 to_addrs 可能包含非 ASCII 字符。此方法可能会引发以下异常:
SMTPRecipientsRefused
所有收件人都被拒绝。 没有人收到邮件。 异常对象的
recipients
属性是一个字典,其中包含有关被拒绝收件人的信息(例如,当至少一个收件人被接受时返回的那个)。SMTPHeloError
服务器没有正确回复
HELO
问候语。SMTPSenderRefused
服务器不接受 from_addr。
SMTPDataError
服务器回复了一个意外的错误代码(除了收件人的拒绝)。
SMTPNotSupportedError
SMTPUTF8
在 mail_options 中给出,但服务器不支持。
除非另有说明,否则即使引发异常,连接也会打开。
3.2 版更改: msg 可能是一个字节串。
3.5 版本变更:添加了
SMTPUTF8
支持,如果指定了SMTPUTF8
但服务器不支持,可能会引发 SMTPNotSupportedError。
- SMTP.send_message(msg, from_addr=None, to_addrs=None, mail_options=(), rcpt_options=())
这是一种使用 email.message.Message 对象表示的消息调用 sendmail() 的便捷方法。 这些参数与 sendmail() 的含义相同,除了 msg 是一个
Message
对象。如果 from_addr 是
None
或 to_addrs 是None
,则send_message
用从 的标头中提取的地址填充这些参数RFC 5322 中指定的 msg:from_addr 设置为 Sender 字段(如果存在),否则设置为来自字段。 to_addrs 组合了来自 msg 的 To、Cc 和 Bcc 字段的值(如果有的话)。 如果消息中正好出现一组 Resent-* 标头,则忽略常规标头并使用 Resent-* 标头。 如果消息包含一组以上的 Resent-* 标头,则会引发 ValueError,因为无法明确检测最近的一组 Resent-[ X190X] 标头。send_message
使用 BytesGenerator 和\r\n
作为 linesep 序列化 msg,并调用 sendmail()传输结果消息。 无论 from_addr 和 to_addrs 的值如何,send_message
都不会传输任何 Bcc 或 Resent-Bcc 标头可能出现在 msg 中。 如果 from_addr 和 to_addrs 中的任何地址包含非 ASCII 字符并且服务器不通告SMTPUTF8
支持,则会引发SMTPNotSupported
错误. 否则,Message
将使用其 策略 的克隆序列化,其中 utf8 属性设置为True
,以及SMTPUTF8
和 [ X153X] 添加到 mail_options。3.2 版中的新功能。
3.5 新功能:支持国际化地址(
SMTPUTF8
)。
- SMTP.quit()
- 终止 SMTP 会话并关闭连接。 返回 SMTP
QUIT
命令的结果。
对应于标准 SMTP/ESMTP 命令的低级方法 HELP
、RSET
、NOOP
、MAIL
、RCPT
和 DATA
也支持。 通常这些不需要直接调用,所以这里没有记录。 详情请查阅模块代码。
21.17.2. SMTP 示例
此示例提示用户输入邮件信封中所需的地址(“收件人”和“发件人”地址)以及要传递的邮件。 请注意,要包含在消息中的标题必须包含在输入的消息中; 此示例不对 RFC 822 标头进行任何处理。 特别是,“收件人”和“发件人”地址必须明确包含在邮件头中。
import smtplib
def prompt(prompt):
return input(prompt).strip()
fromaddr = prompt("From: ")
toaddrs = prompt("To: ").split()
print("Enter message, end with ^D (Unix) or ^Z (Windows):")
# Add the From: and To: headers at the start!
msg = ("From: %s\r\nTo: %s\r\n\r\n"
% (fromaddr, ", ".join(toaddrs)))
while True:
try:
line = input()
except EOFError:
break
if not line:
break
msg = msg + line
print("Message length is", len(msg))
server = smtplib.SMTP('localhost')
server.set_debuglevel(1)
server.sendmail(fromaddr, toaddrs, msg)
server.quit()