smtplib — SMTP 協議客戶端

原始碼: Lib/smtplib.py


smtplib 模組定義了一個 SMTP 客戶端會話物件,該物件可用於將郵件傳送到任何具有 SMTP 或 ESMTP 監聽守護程序的網際網路機器。有關 SMTP 和 ESMTP 操作的詳細資訊,請參閱 RFC 821 (Simple Mail Transfer Protocol) 和 RFC 1869 (SMTP Service Extensions)。

可用性:非 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`,其引數為 `self` 和 `data`,其中 `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(也是可選的)可以包含一個 ssl.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 客戶端。通常使用 Unix 套接字進行 LMTP,因此我們的 connect() 方法必須同時支援 Unix 套接字和常規的 host:port 伺服器。可選引數 local_hostnamesource_address 的含義與 SMTP 類中的相同。要指定 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 - Simple Mail Transfer Protocol

SMTP 的協議定義。本文件涵蓋了 SMTP 的模型、操作過程和協議細節。

RFC 1869 - SMTP Service Extensions

SMTP 擴充套件的 ESMTP 定義。這描述了一個用於擴充套件 SMTP 的新命令的框架,支援伺服器提供的命令的動態發現,並定義了一些額外的命令。

SMTP 物件

SMTP 例項具有以下方法

SMTP.set_debuglevel(level)

設定除錯輸出級別。對於 level,值為 1 或 True 會導致有關連線以及傳送到伺服器和從伺服器接收到的所有訊息的除錯訊息。值為 2 會導致這些訊息帶有時間戳。

版本 3.5 中已更改: 添加了除錯級別 2。

SMTP.docmd(cmd, args='')

向伺服器傳送命令 cmd。可選引數 args 僅用空格將其與命令連線起來。

此方法返回一個由數字響應程式碼和實際響應行組成的 2 元組(多行響應會合併為一行)。

正常操作中,通常不需要顯式呼叫此方法。它用於實現其他方法,並且可能對測試私有擴充套件有用。

如果在等待回覆時與伺服器的連線丟失,將引發 SMTPServerDisconnected

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

連線到給定埠上的主機。預設值是連線到本地主機上的標準 SMTP 埠 (25)。如果主機名以冒號 (`:`) 加上數字結尾,則會剝離該字尾,並將該數字解釋為要使用的埠號。如果在例項化時指定了主機,則建構函式會自動呼叫此方法。返回伺服器在其連線響應中傳送的響應程式碼和訊息組成的 2 元組。

引發一個 審計事件 `smtplib.connect`,其引數為 `self`、`host` 和 `port`。

SMTP.helo(name='')

使用 HELO 向 SMTP 伺服器標識自己。主機名引數預設為本地主機的完全限定域名。伺服器返回的訊息儲存在物件的 helo_resp 屬性中。

正常操作中,通常不需要顯式呼叫此方法。它將在必要時由 sendmail() 隱式呼叫。

SMTP.ehlo(name='')

使用 EHLO 向 ESMTP 伺服器標識自己。主機名引數預設為本地主機的完全限定域名。檢查響應以獲取 ESMTP 選項,併為 has_extn() 儲存它們。此外,還設定了幾個資訊屬性:伺服器返回的訊息儲存在物件的 ehlo_resp 屬性中,does_esmtp 設定為 TrueFalse(取決於伺服器是否支援 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 指定對於支援它的身份驗證方法,是否可以將 RFC 4954 中指定的“初始響應”與 AUTH 命令一起傳送,而不是要求質詢/響應。

版本 3.5 中已更改: 可能會引發 SMTPNotSupportedError,並添加了 initial_response_ok 引數。

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

發出指定的身份驗證 mechanismSMTP AUTH 命令,並透過 authobject 處理質詢響應。

mechanism 指定用作 AUTH 命令引數的身份驗證機制;有效值是 esmtp_features 中 `auth` 元素列出的值。

authobject 必須是一個可呼叫物件,它接受一個可選的單個引數

data = authobject(challenge=None)

如果可選關鍵字引數 initial_response_ok 為 true,則會先用無引數呼叫 `authobject()`。它可以返回 RFC 4954 的“初始響應”ASCII str,它將作為下面的 AUTH 命令進行編碼和傳送。如果 `authobject()` 不支援初始響應(例如,因為它需要質詢),則在呼叫時應返回 None(當 `challenge=None` 時)。如果 initial_response_ok 為 false,則不會先用 `None` 呼叫 `authobject()`。

如果初始響應檢查返回 None,或者 initial_response_ok 為 false,則將呼叫 `authobject()` 來處理伺服器的質詢響應;傳遞給它的 challenge 引數將是一個 bytes。它應該返回 ASCII str 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(*, context=None)

將 SMTP 連線置於 TLS (Transport Layer Security) 模式。之後的所有 SMTP 命令都將進行加密。之後應再次呼叫 ehlo()

如果提供了 keyfilecertfile,則它們用於建立 ssl.SSLContext

可選的 context 引數是一個 ssl.SSLContext 物件;這是使用 keyfile 和 certfile 的替代方法,如果指定了它,則 keyfilecertfile 都應為 None

如果本會話中沒有先前的 EHLOHELO 命令,則此方法會先嚐試 ESMTP EHLO

在 3.12 版更改: 已移除已棄用的 keyfilecertfile 引數。

SMTPHeloError

伺服器對 HELO 問候語沒有正確響應。

SMTPNotSupportedError

伺服器不支援 STARTTLS 擴充套件。

RuntimeError

您的 Python 直譯器沒有可用的 SSL/TLS 支援。

版本 3.3 中已更改: 添加了 context 引數。

版本 3.4 中已更改: 該方法現在支援使用 ssl.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_options 傳遞給所有 `RCPT` 命令的 ESMTP 選項(例如 `DSN` 命令)。(如果需要對不同收件人使用不同的 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

所有收件人都被拒絕。沒有人收到郵件。

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

這是一個方便的方法,用於呼叫 sendmail(),其中訊息由 email.message.Message 物件表示。引數的含義與 sendmail() 相同,只是 msg 是一個 `Message` 物件。

如果 from_addrNoneto_addrsNone,則 `send_message` 會根據 RFC 5322 中指定的 msg 的頭部欄位提取地址來填充這些引數:from_addr 設定為 `Sender` 欄位(如果存在),否則設定為 `From` 欄位。to_addrs 組合了 msg 中 `To`、`Cc` 和 `Bcc` 欄位的值(如果有)。如果訊息中存在恰好一對 `Resent-*` 頭部,則忽略常規頭部,而是使用 `Resent-*` 頭部。如果訊息包含多對 `Resent-*` 頭部,則會引發 ValueError,因為無法明確檢測到最近的 `Resent-` 頭部集。

`send_message` 使用 BytesGeneratormsg 序列化,將 `\r\n` 作為 *linesep*,並呼叫 sendmail() 來傳輸生成的郵件。無論 from_addrto_addrs 的值如何,`send_message` 都不會傳輸 msg 中可能存在的任何 `Bcc` 或 `Resent-Bcc` 頭部。如果 from_addrto_addrs 中的任何地址包含非 ASCII 字元且伺服器未宣告 `SMTPUTF8` 支援,則會引發 SMTPNotSupportedError。否則,將使用其 policy 的克隆(將 utf8 屬性設定為 True)來序列化 `Message`,並將 `SMTPUTF8` 和 `BODY=8BITMIME` 新增到 mail_options 中。

在 3.2 版本加入。

版本 3.5 中已新增: 支援國際化地址(`SMTPUTF8`)。

SMTP.quit()

終止 SMTP 會話並關閉連線。返回 SMTP QUIT 命令的結果。

還支援與標準 SMTP/ESMTP 命令 `HELP`、`RSET`、`NOOP`、`MAIL`、`RCPT` 和 `DATA` 對應的低階方法。通常不需要直接呼叫它們,因此此處不予記錄。有關詳細資訊,請參閱模組程式碼。

此外,SMTP 例項具有以下屬性

SMTP.helo_resp

HELO 命令的響應,參見 helo()

SMTP.ehlo_resp

EHLO 命令的響應,參見 ehlo()

SMTP.does_esmtp

一個布林值,指示伺服器是否支援 ESMTP,參見 ehlo()

SMTP.esmtp_features

一個字典,包含伺服器支援的 SMTP 服務副檔名稱,參見 ehlo()

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: 示例