smtplib --- SMTP 協(xié)議客戶端?

源代碼: Lib/smtplib.py


smtplib 模塊定義了一個 SMTP 客戶端會話對象,該對象可將郵件發(fā)送到互聯(lián)網上任何帶有 SMTP 或 ESMTP 監(jiān)聽程序的計算機。 關于 SMTP 和 ESMTP 操作的更多細節(jié)請參閱 RFC 821 (簡單郵件傳輸協(xié)議) 和 RFC 1869 (SMTP 服務擴展)。

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

SMTP 實例是對 SMTP 連接的封裝。 它提供了支持各種 SMTP 和 ESMTP 操作的方法。 如果給出了可選的 host 和 port 形參,則會在初始化期間調用 SMTP connect() 方法并附帶這些形參。 如果指定了 local_hostname,它將在 HELO/EHLO 命令中被用作本地主機的 FQDN。 在其他情況下,會使用 socket.getfqdn() 來找到本地主機名。 如果 connect() 調用返回了表示成功的代碼以外的信息,則會引發(fā) SMTPConnectError。 可選的 timeout 形參指定了阻塞操作如連接嘗試的超時秒數(shù)(如果未指定,則將使用全局默認超時設置)。 如果達到超時限制,則會引發(fā) TimeoutError。 可選的 source_address 形參允許在在有多張網卡的計算機中綁定到某些特定的源地址,和/或綁定到某些特定的源 TCP 端口。 它接受一個 2 元組 (host, port) 作為在連接之前所綁定作為其源地址的套接字。 如果省略(或者如果 host 或 port 為 '' 和/或分別為 0)則將使用 OS 的默認行為。

正常使用時,只需要初始化或 connect 方法,sendmail() 方法,再加上 SMTP.quit() 方法即可。下文包括了一個示例。

SMTP 類支持 with 語句。當這樣使用時,with 語句一退出就會自動發(fā)出 SMTP QUIT 命令。例如:

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

引發(fā)一個 審計事件 smtplib.send,附帶參數(shù) self, data。

在 3.3 版更改: 添加了對 with 語句的支持。

在 3.3 版更改: 添加了 source_address 參數(shù)。

3.5 新版功能: 現(xiàn)在已支持 SMTPUTF8 擴展 (RFC 6531)。

在 3.9 版更改: 如果 timeout 形參被設為零,則它將引發(fā) ValueError 來阻止創(chuàng)建非阻塞的套接字

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

SMTP_SSL 實例與 SMTP 實例的行為完全相同。在開始連接就需要 SSL,且 starttls() 不適合的情況下,應該使用 SMTP_SSL。如果未指定 host,則使用 localhost。如果 port 為 0,則使用標準 SMTP-over-SSL 端口(465)??蛇x參數(shù) local_hostname、timeoutsource_address 的含義與 SMTP 類中的相同。可選參數(shù) context 是一個 SSLContext 對象,可以從多個方面配置安全連接。請閱讀 安全考量 以獲取最佳實踐。

keyfilecertfilecontext 的傳統(tǒng)替代物,它們可以指向 PEM 格式的私鑰和證書鏈文件用于 SSL 連接。

在 3.3 版更改: 增加了 context。

在 3.3 版更改: 添加了 source_address 參數(shù)。

在 3.4 版更改: 本類現(xiàn)在支持使用 ssl.SSLContext.check_hostname服務器名稱指示 (參閱 ssl.HAS_SNI)進行主機名檢查。

3.6 版后已移除: keyfilecertfile 已棄用并轉而推薦 context。 請改用 ssl.SSLContext.load_cert_chain() 或讓 ssl.create_default_context() 為你選擇系統(tǒng)所信任的 CA 證書。

在 3.9 版更改: 如果 timeout 形參被設為零,則它將引發(fā) ValueError 來阻止創(chuàng)建非阻塞的套接字

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

LMTP 協(xié)議與 ESMTP 非常相似,它很大程度上基于標準的 SMTP 客戶端。將 Unix 套接字用于 LMTP 是很常見的,因此 connect() 方法支持 Unix 套接字,也支持常規(guī)的 host:port 服務器??蛇x參數(shù) local_hostname 和 source_address 的含義與 SMTP 類中的相同。要指定 Unix 套接字,host 必須使用絕對路徑,以 '/' 開頭。

支持使用常規(guī)的 SMTP 機制來進行認證。 當使用 Unix 套接字時,LMTP 通常不支持或要求任何認證,但你的情況可能會有所不同。

在 3.9 版更改: 添加了可選的 timeout 形參。

同樣地定義了一組精心選擇的異常:

exception smtplib.SMTPException?

OSError 的子類,它是本模塊提供的所有其他異常的基類。

在 3.4 版更改: SMTPException 已成為 OSError 的子類

exception smtplib.SMTPServerDisconnected?

當服務器意外斷開連接,或在 SMTP 實例連接到服務器之前嘗試使用它時將引發(fā)此異常。

exception smtplib.SMTPResponseException?

包括 SMTP 錯誤代碼的所有異常的基類。 這些異常會在 SMTP 服務器返回錯誤代碼時在實例中生成。 錯誤代碼存放在錯誤的 smtp_code 屬性中,并且 smtp_error 屬性會被設為錯誤消息。

exception smtplib.SMTPSenderRefused?

發(fā)送方地址被拒絕。 除了在所有 SMTPResponseException 異常上設置的屬性,還會將 'sender' 設為代表拒絕方 SMTP 服務器的字符串。

exception smtplib.SMTPRecipientsRefused?

所有接收方地址被拒絕。 每個接收方的錯誤可通過屬性 recipients 來訪問,該屬性是一個字典,其元素順序與 SMTP.sendmail() 所返回的一致。

exception smtplib.SMTPDataError?

SMTP 服務器拒絕接收消息數(shù)據(jù)。

exception smtplib.SMTPConnectError?

在建立與服務器的連接期間發(fā)生了錯誤。

exception smtplib.SMTPHeloError?

服務器拒絕了我們的 HELO 消息。

exception smtplib.SMTPNotSupportedError?

嘗試的命令或選項不被服務器所支持。

3.5 新版功能.

exception smtplib.SMTPAuthenticationError?

SMTP 認證出現(xiàn)問題。 最大的可能是服務器不接受所提供的用戶名/密碼組合。

參見

RFC 821 - 簡單郵件傳輸協(xié)議

SMTP 的協(xié)議定義。 該文件涵蓋了 SMTP 的模型、操作程序和協(xié)議細節(jié)。

RFC 1869 - SMTP 服務擴展

定義了 SMTP 的 ESMTP 擴展。 這描述了一個用新命令擴展 SMTP 的框架,支持動態(tài)發(fā)現(xiàn)服務器所提供的命令,并定義了一些額外的命令。

SMTP 對象?

一個 SMTP 實例擁有以下方法:

SMTP.set_debuglevel(level)?

設置調試輸出級別。 如果 level 的值為 1 或 True ,就會產生連接的調試信息,以及所有發(fā)送和接收服務器的信息。 如果 level 的值為 2 ,則這些信息會被加上時間戳。

在 3.5 版更改: 添調試級別 2 。

SMTP.docmd(cmd, args='')?

向服務器發(fā)送一條命令 cmd 。 可選的參數(shù) args 被簡單地串聯(lián)到命令中,用一個空格隔開。

這將返回一個由數(shù)字響應代碼和實際響應行組成的2元組(多行響應被連接成一個長行)。

在正常操作中,應該沒有必要明確地調用這個方法。它被用來實現(xiàn)其他方法,對于測試私有擴展可能很有用。

如果在等待回復的過程中,與服務器的連接丟失, SMTPServerDisconnected 將被觸發(fā)。

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

連接到某個主機的某個端口。默認是連接到 localhost 的標準 SMTP 端口(25)上。如果主機名以冒號 (':') 結尾,后跟數(shù)字,則該后綴將被刪除,且數(shù)字將視作要使用的端口號。如果在實例化時指定了 host,則構造函數(shù)會自動調用本方法。返回包含響應碼和響應消息的 2 元組,它們由服務器在其連接響應中發(fā)送。

觸發(fā)一個 auditing event smtplib.connect,其參數(shù)為 self , hostport 。

SMTP.helo(name='')?

使用 HELO 向 SMTP 服務器表明自己的身份。 hostname 參數(shù)默認為本地主機的完全合格域名。服務器返回的消息被存儲為對象的 helo_resp 屬性。

在正常操作中,應該沒有必要明確調用這個方法。它將在必要時被 sendmail() 隱式調用。

SMTP.ehlo(name='')?

使用 EHLO 向 ESMTP 服務器表明自己的身份。 hostname 參數(shù)默認為本地主機的完全合格域名。 檢查 ESMTP 選項的響應,并存儲它們供 has_extn() 使用。同時設置幾個信息屬性:服務器返回的消息被存儲為 ehlo_resp 屬性, does_esmtp 根據(jù)服務器是否支持 ESMTP 被設置為 TrueFalse ,而 esmtp_features 將是一個字典,包含這個服務器支持的 SMTP 服務擴展的名稱,以及它們的參數(shù)(如果有)。

除非你想在發(fā)送郵件前使用 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 服務器。 參數(shù)是用于認證的用戶名和密碼。 如果會話在之前沒有執(zhí)行過 EHLOHELO 命令,此方法會先嘗試 ESMTP EHLO。 如果認證成功則此方法將正常返回,否則可能引發(fā)以下異常:

SMTPHeloError

服務器沒有正確回復 HELO 問候。

SMTPAuthenticationError

服務器不接受所提供的用戶名/密碼組合。

SMTPNotSupportedError

服務器不支持 AUTH 命令。

SMTPException

未找到適當?shù)恼J證方法。

smtplib 所支持的每種認證方法只要被服務器聲明支持就會被依次嘗試。 請參閱 auth() 獲取受支持的認證方法列表。 initial_response_ok 會被傳遞給 auth()。

可選的關鍵字參數(shù) initial_response_ok 對于支持它的認證方法,是否可以與 AUTH 命令一起發(fā)送 RFC 4954 中所規(guī)定的“初始響應”,而不是要求回復/響應。

在 3.5 版更改: 可能會引發(fā) SMTPNotSupportedError,并添加 initial_response_ok 形參。

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

為指定的認證機制 mechanism 發(fā)送 SMTP AUTH 命令,并通過 authobject 處理回復響應。

mechanism 指定要使用何種認證機制作為 AUTH 命令的參數(shù);可用的值是在 esmtp_featuresauth 元素中列出的內容。

authobject 必須是接受一個可選的單獨參數(shù)的可調用對象:

data = authobject(challenge=None)

如果可選的關鍵字參數(shù) initial_response_ok 為真值,則將先不帶參數(shù)地調用 authobject()。 它可以返回 RFC 4954 "初始響應" ASCII str,其內容將被編碼并使用下述的 AUTH 命令來發(fā)送。 如果 authobject() 不支持初始響應(例如由于要求一個回復),它應當將 None 作為附帶 challenge=None 調用的返回值。 如果 initial_response_ok 為假值,則 authobject() 將不會附帶 None 被首先調用。

如果初始響應檢測返回了 None,或者如果 initial_response_ok 為假值,則將調用 authobject() 來處理服務器的回復響應;它所傳遞的 challenge 參數(shù)將為一個 bytes。 它應當返回用 base64 進行編碼的 ASCII str data 并發(fā)送給服務器。

SMTP 類提供的 authobjects 針對 CRAM-MD5, PLAINLOGIN 等機制;它們的名稱分別是 SMTP.auth_cram_md5, SMTP.auth_plainSMTP.auth_login。 它們都要求將 userpassword 這兩個 SMTP 實例屬性設為適當?shù)闹怠?/p>

用戶代碼通常不需要直接調用 auth,而是調用 login() 方法,它將按上述順序依次嘗試上述每一種機制。 auth 被公開以便輔助實現(xiàn) smtplib 沒有(或尚未)直接支持的認證方法。

3.5 新版功能.

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

將 SMTP 連接設為 TLS (傳輸層安全) 模式。 后續(xù)的所有 SMTP 命令都將被加密。 你應當隨即再次調用 ehlo()。

如果提供了 keyfilecertfile,它們會被用來創(chuàng)建 ssl.SSLContext

可選的 context 形參是一個 ssl.SSLContext 對象;它是使用密鑰文件和證書的替代方式,如果指定了該形參則 keyfilecertfile 都應為 None。

如果這個會話中沒有先前的 EHLO or HELO 命令,該方法會首先嘗試 ESMTP EHLO。

3.6 版后已移除: keyfilecertfile 已棄用并轉而推薦 context。 請改用 ssl.SSLContext.load_cert_chain() 或讓 ssl.create_default_context() 為你選擇系統(tǒng)所信任的 CA 證書。

SMTPHeloError

服務器沒有正確回復 HELO 問候。

SMTPNotSupportedError

服務器不支持 STARTTLS 擴展。

RuntimeError

SSL/TLS 支持在你的 Python 解釋器上不可用。

在 3.3 版更改: 增加了 context。

在 3.4 版更改: 此方法現(xiàn)在支持使用 SSLContext.check_hostname服務器名稱指示符 (參見 HAS_SNI) 進行主機名檢查。

在 3.5 版更改: 因缺少 STARTTLS 支持而引發(fā)的錯誤現(xiàn)在是 SMTPNotSupportedError 子類而不是 SMTPException 基類。

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

發(fā)送郵件。必要參數(shù)是一個 RFC 822 發(fā)件地址字符串,一個 RFC 822 收件地址字符串列表(裸字符串將被視為含有 1 個地址的列表),以及一個消息字符串。調用者可以將 ESMTP 選項列表(如 8bitmime)作為 mail_options 傳入,用于 MAIL FROM 命令。需要與所有 RCPT 命令一起使用的 ESMTP 選項(如 DSN 命令)可以作為 rcpt_options 傳入。(如果需要對不同的收件人使用不同的 ESMTP 選項,則必須使用底層的方法來發(fā)送消息,如 mail(), rcpt()data()。)

備注

from_addrto_addrs 形參被用來構造傳輸代理所使用的消息封包。 sendmail 不會以任何方式修改消息標頭。

msg 可以是一個包含 ASCII 范圍內字符的字符串,或是一個字節(jié)串。 字符串會使用 ascii 編解碼器編碼為字節(jié)串,并且單獨的 \r\n 字符會被轉換為 \r\n 字符序列。 字節(jié)串則不會被修改。

如果在此之前本會話沒有執(zhí)行過 EHLOHELO 命令,此方法會先嘗試 ESMTP EHLO。 如果服務器執(zhí)行了 ESMTP,消息大小和每個指定的選項將被傳遞給它(如果指定的選項屬于服務器聲明的特性集)。 如果 EHLO 失敗,則將嘗試 HELO 并屏蔽 ESMTP 選項。

如果郵件被至少一個接收方接受則此方法將正常返回。 在其他情況下它將引發(fā)異常。 也就是說,如果此方法沒有引發(fā)異常,則應當會有人收到你的郵件。 如果此方法沒有引發(fā)異常,它將返回一個字典,其中的條目對應每個拒絕的接收方。 每個條目均包含由服務器發(fā)送的 SMTP 錯誤代碼和相應錯誤消息所組成的元組。

如果 SMTPUTF8 包括在 mail_options 中,并且被服務器所支持,則 from_addrto_addrs 可能包含非 ASCII 字符。

此方法可能引發(fā)以下異常:

SMTPRecipientsRefused

所有收件人都被拒絕。 無人收到郵件。 該異常的 recipients 屬性是一個字典,其中有被拒絕收件人的信息(類似于至少有一個收件人接受郵件時所返回的信息)。

SMTPHeloError

服務器沒有正確回復 HELO 問候。

SMTPSenderRefused

服務器不接受 from_addr。

SMTPDataError

服務器回復了一個意外的錯誤代碼(而不是拒絕收件人)。

SMTPNotSupportedError

mail_options 中給出了 SMTPUTF8 但是不被服務器所支持。

除非另有說明,即使在引發(fā)異常之后連接仍將被打開。

在 3.2 版更改: msg 可以為字節(jié)串。

在 3.5 版更改: 增加了 SMTPUTF8 支持,并且如果指定了 SMTPUTF8 但是不被服務器所支持則可能會引發(fā) SMTPNotSupportedError。

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

本方法是一種快捷方法,用于帶著消息調用 sendmail(),消息由 email.message.Message 對象表示。參數(shù)的含義與 sendmail() 中的相同,除了 msg,它是一個 Message 對象。

如果 from_addrNoneto_addrsNone,那么``send_message``將根據(jù) RFC 5322,從 msg 頭部提取地址填充下列參數(shù):如果頭部存在 Sender 字段,則用它填充 from_addr,不存在則用 From 字段填充 from_addrto_addrs 組合了 msg 中的 To, CcBcc 字段的值(字段存在的情況下)。如果一組 Resent-* 頭部恰好出現(xiàn)在 message 中,那么就忽略常規(guī)的頭部,改用 Resent-* 頭部。如果 message 包含多組 Resent-* 頭部,則引發(fā) ValueError,因為無法明確檢測出哪一組 Resent- 頭部是最新的。

send_message 使用 BytesGenerator 來序列化 msg,且將 \r\n 作為 linesep,并調用 sendmail() 來傳輸序列化后的結果。無論 from_addrto_addrs 的值為何,send_message 都不會傳輸 msg 中可能出現(xiàn)的 BccResent-Bcc 頭部。如果 from_addrto_addrs 中的某個地址包含非 ASCII 字符,且服務器沒有聲明支持 SMTPUTF8,則引發(fā) SMTPNotSupported 錯誤。如果服務器支持,則 Message 將按新克隆的 policy 進行序列化,其中的 utf8 屬性被設置為 True,且 SMTPUTF8BODY=8BITMIME 被添加到 mail_options 中。

3.2 新版功能.

3.5 新版功能: 支持國際化地址 (SMTPUTF8)。

SMTP.quit()?

終結 SMTP 會話并關閉連接。 返回 SMTP QUIT 命令的結果。

與標準 SMTP/ESMTP 命令 HELP, RSET, NOOP, MAIL, RCPTDATA 對應的低層級方法也是受支持的。 通常不需要直接調用這些方法,因此它們沒有被寫入本文檔。 相關細節(jié)請參看模塊代碼。

SMTP 示例?

這個例子提示用戶輸入消息封包所需的地址 ('To' 和 'From' 地址),以及所要封包的消息。 請注意包括在消息中的標頭必須包括在輸入的消息中;這個例子不對 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() 來發(fā)送它,參見 email: 示例。