Skip to main content

21.17. smtplib — SMTP协议客户端

源代码: Lib/smtplib.py


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()quit() 方法。下面包含一个例子。

SMTP 类支持 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 SSL over SSL端口(465)。可选参数 local_hostnametimeoutsource_address 具有与它们在 SMTP 类中相同的含义。 context 也是可选的,可以包含 SSLContext,并允许配置安全连接的各个方面。请阅读 安全注意事项 的最佳做法。

keyfilecertfilecontext 的传统替代,可以指向用于SSL连接的PEM格式的私钥和证书链文件。

在 3.3 版更改: 加入 context

在 3.3 版更改: source_address参数。

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

3.6 版后已移除: keyfilecertfile 不赞成使用 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客户端。通常使用Unix套接字为LMTP,所以我们的 connect() 方法必须支持,以及一个常规的主机:端口服务器。可选参数local_hostname和source_address具有与它们在 SMTP 类中相同的含义。要指定Unix套接字,必须使用 host 的绝对路径,以“/”开头。

支持使用常规SMTP机制的认证。当使用Unix套接字时,LMTP通常不支持或需要任何身份验证,但您的里程可能会有所不同。

一个很好的异常选择也被定义:

exception smtplib.SMTPException

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

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

exception smtplib.SMTPServerDisconnected

当服务器意外断开连接或在将 SMTP 实例连接到服务器之前尝试使用 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,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 指定对于支持它的身份验证方法,可以与 AUTH 命令一起发送 RFC 4954 中指定的“初始响应”,而不是要求挑战/响应。

在 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 为真,authobject() 将首先被调用没有参数。它可以返回 RFC 4954 “初始响应”字节,它将被编码并与 AUTH 命令一起发送,如下所示。如果 authobject() 不支持初始响应(例如,因为它需要挑战),则当用 challenge=None 调用时应当返回 None。如果 initial_response_ok 为假,则 authobject() 不会首先被调用 None

如果初始响应检查返回 None,或者如果 initial_response_ok 为假,则将调用 authobject() 来处理服务器的质询响应;它通过的 challenge 参数将是 bytes。它应该返回将被base64编码并发送到服务器的 bytes data

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

用户代码通常不需要直接调用 auth,而是可以调用 login() 方法,它将按照列出的顺序轮流尝试上述每个机制。 auth 被暴露以促进未(或尚未)由 smtplib 直接支持的认证方法的实现。

3.5 新版功能.

SMTP.starttls(keyfile=None, certfile=None, context=None)

将SMTP连接设置为TLS(传输层安全)模式。随后的所有SMTP命令将被加密。然后您应该再次呼叫 ehlo()

如果提供了 keyfilecertfile,则将这些传递给 socket 模块的 ssl() 功能。

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

如果没有以前的 EHLOHELO 命令此会话,此方法首先尝试ESMTP EHLO

SMTPHeloError

服务器未正确回复 HELO 问候语。

SMTPNotSupportedError

服务器不支持STARTTLS扩展。

RuntimeError

SSL/TLS支持不适用于您的Python解释器。

在 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个地址的列表)和消息字符串。呼叫者可以将要在 MAIL FROM 命令中使用的ESMTP选项(例如 8bitmime)的列表传递为 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错误代码的元组和服务器发送的附带的错误消息。

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

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

SMTPRecipientsRefused

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

SMTPHeloError

服务器未正确回复 HELO 问候语。

SMTPSenderRefused

服务器不接受 from_addr

SMTPDataError

服务器回复了意外的错误代码(除了收件人拒绝)。

SMTPNotSupportedError

SMTPUTF8mail_options 中给出,但服务器不支持。

除非另有说明,否则即使发生异常也将打开连接。

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

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

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

这是一个方便的方法,用于用由 email.message.Message 对象表示的消息调用 sendmail()。参数具有与 sendmail() 相同的含义,除了 msgMessage 对象。

如果 from_addrNoneto_addrsNone,则 send_message 使用从 RFC 5322 中指定的从 msg 的报头提取的地址填充那些参数:如果 Sender 字段存在,则将 from_addr 设置为 Sender 字段,否则将其设置到 From 字段。 to_addrs 组合来自 msgToCcBcc 字段的值(如果有的话)。如果消息中只出现一组 Resent-* 头,那么将忽略常规头,而是使用 Resent-* 头。如果消息包含多于一组 Resent-* 报头,则引发 ValueError,因为没有办法明确地检测最近的一组 Resent- 报头。

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

3.2 新版功能.

3.5 新版功能: 支持国际化地址(SMTPUTF8)。

SMTP.quit()

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

还支持对应于标准SMTP/ESMTP命令 HELPRSETNOOPMAILRCPTDATA 的低级方法。通常这些不需要直接调用,因此在这里没有记录。有关详细信息,请参阅模块代码。

21.17.2. SMTP示例

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

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()

注解

一般来说,您将需要使用 email 包的功能来构造电子邮件,然后您可以通过 send_message() 发送;见 email:示例