email.message.Message:使用 compat32 API 表示電子郵件訊息

Message 類與 EmailMessage 類非常相似,只是它沒有該類新增的方法,並且某些其他方法的預設行為略有不同。我們還在這裡記錄了一些 EmailMessage 類支援的方法,除非您正在處理遺留程式碼,否則不建議使用它們。

這兩個類的理念和結構其他方面是相同的。

本文件描述了在 Message 的預設策略 Compat32 下的行為。如果要使用其他策略,則應改用 EmailMessage 類。

電子郵件訊息由標頭負載組成。標頭必須是RFC 5322 樣式的名稱和值,其中欄位名稱和值用冒號分隔。冒號不是欄位名稱或欄位值的一部分。負載可以是簡單的文字訊息,二進位制物件,也可以是子訊息的結構化序列,每個子訊息都有自己的標頭集和自己的負載。後一種負載型別透過訊息具有諸如 multipart/*message/rfc822 的 MIME 型別來表示。

Message 物件提供的概念模型是一個有序的標頭字典,其中包含訪問標頭中特定資訊、訪問負載、生成訊息的序列化版本以及遞迴遍歷物件樹的其他方法。請注意,支援重複的標頭,但必須使用特殊方法來訪問它們。

Message 偽字典透過標頭名稱進行索引,標頭名稱必須是 ASCII 值。字典的值是應該僅包含 ASCII 字元的字串;對於非 ASCII 輸入有一些特殊處理,但它並不總是產生正確的結果。標頭以保留大小寫的形式儲存和返回,但欄位名稱不區分大小寫地匹配。還可能存在單個信封標頭,也稱為 Unix-From 標頭或 From_ 標頭。負載可以是字串或位元組(對於簡單訊息物件),也可以是 Message 物件的列表(對於 MIME 容器文件,例如 multipart/*message/rfc822)。

以下是 Message 類的方法

class email.message.Message(policy=compat32)

如果指定了 policy (它必須是 policy 類的例項),則使用其指定的規則來更新和序列化訊息的表示形式。如果未設定 policy,則使用 compat32 策略,該策略保持與 Python 3.2 版本電子郵件包的向後相容性。有關更多資訊,請參閱 policy 文件。

在 3.3 版本中更改: 添加了 policy 關鍵字引數。

as_string(unixfrom=False, maxheaderlen=0, policy=None)

將整個訊息作為字串展平返回。當可選的 unixfrom 為 true 時,信封標頭將包含在返回的字串中。unixfrom 預設為 False。出於向後相容性的原因,maxheaderlen 預設為 0,因此如果您想要不同的值,則必須顯式覆蓋它(策略中為 max_line_length 指定的值將在此方法中被忽略)。可以使用 policy 引數來覆蓋從訊息例項獲取的預設策略。這可用於控制該方法生成的一些格式,因為指定的 policy 將傳遞給 Generator

展平訊息可能會觸發對 Message 的更改,如果需要填充預設值以完成到字串的轉換(例如,可以生成或修改 MIME 邊界)。

請注意,此方法作為一種便利方法提供,可能並不總是以您想要的方式格式化訊息。例如,預設情況下,它不會對以 From 開頭的行進行 Unix mbox 格式所需的處理。為了獲得更大的靈活性,請例項化一個 Generator 例項,並直接使用其 flatten() 方法。例如

from io import StringIO
from email.generator import Generator
fp = StringIO()
g = Generator(fp, mangle_from_=True, maxheaderlen=60)
g.flatten(msg)
text = fp.getvalue()

如果訊息物件包含未根據 RFC 標準編碼的二進位制資料,則不合規的資料將被 Unicode“未知字元”程式碼點替換。(另請參閱 as_bytes()BytesGenerator。)

在 3.4 版本中更改: 添加了 policy 關鍵字引數。

__str__()

等效於 as_string()。允許 str(msg) 生成包含格式化訊息的字串。

as_bytes(unixfrom=False, policy=None)

返回整個訊息的扁平化位元組物件。當可選引數 unixfrom 為 true 時,返回的字串中將包含信封頭。unixfrom 預設為 False。可以使用 policy 引數來覆蓋從訊息例項獲取的預設策略。這可以用來控制該方法產生的一些格式,因為指定的 policy 將被傳遞給 BytesGenerator

展平訊息可能會觸發對 Message 的更改,如果需要填充預設值以完成到字串的轉換(例如,可以生成或修改 MIME 邊界)。

請注意,此方法作為便利方法提供,可能並不總是按照您想要的方式格式化訊息。例如,預設情況下,它不會對以 From 開頭的行進行 Unix mbox 格式所需的修改。為了獲得更大的靈活性,請例項化一個 BytesGenerator 例項,並直接使用它的 flatten() 方法。例如

from io import BytesIO
from email.generator import BytesGenerator
fp = BytesIO()
g = BytesGenerator(fp, mangle_from_=True, maxheaderlen=60)
g.flatten(msg)
text = fp.getvalue()

在 3.4 版本中加入。

__bytes__()

等效於 as_bytes()。允許 bytes(msg) 生成一個包含格式化訊息的位元組物件。

在 3.4 版本中加入。

is_multipart()

如果訊息的有效負載是子-Message 物件的列表,則返回 True,否則返回 False。當 is_multipart() 返回 False 時,有效負載應該是一個字串物件(可能是 CTE 編碼的二進位制有效負載)。(請注意,is_multipart() 返回 True 並不一定意味著 “msg.get_content_maintype() == ‘multipart’” 將返回 True。例如,當 Message 的型別為 message/rfc822 時,is_multipart 將返回 True。)

set_unixfrom(unixfrom)

將訊息的信封頭設定為 unixfrom,它應該是一個字串。

get_unixfrom()

返回訊息的信封頭。如果信封頭從未設定,則預設為 None

attach(payload)

將給定的 payload 新增到當前有效負載,在呼叫之前,當前有效負載必須為 NoneMessage 物件的列表。呼叫後,有效負載將始終是 Message 物件的列表。如果要將有效負載設定為標量物件(例如字串),請改用 set_payload()

這是一個遺留方法。在 EmailMessage 類上,其功能被 set_content() 和相關的 makeadd 方法所取代。

get_payload(i=None, decode=False)

返回當前的有效負載,當 is_multipart()True 時,它將是 Message 物件的列表,當 is_multipart()False 時,它將是一個字串。如果有效負載是一個列表,並且您修改了該列表物件,則會就地修改訊息的有效負載。

對於可選引數 i,如果 is_multipart()Trueget_payload() 將返回有效負載的第 i 個元素,從零開始計數。如果 i 小於 0 或大於等於有效負載中的專案數,則會引發 IndexError。如果有效負載是一個字串(即 is_multipart()False)並且給定了 i,則會引發 TypeError

可選引數 decode 是一個標誌,指示是否應根據 Content-Transfer-Encoding 標頭對有效負載進行解碼。當 True 且訊息不是多部分時,如果此標頭的值為 quoted-printablebase64,則將對有效負載進行解碼。如果使用其他編碼,或者缺少 Content-Transfer-Encoding 標頭,則按原樣(未解碼)返回有效負載。在所有情況下,返回的值都是二進位制資料。如果訊息是多部分且 decode 標誌為 True,則返回 None。如果有效負載是 base64 格式的,並且格式不完美(缺少填充、base64 字母表之外的字元),則會將相應的缺陷新增到訊息的缺陷屬性中(分別為 InvalidBase64PaddingDefectInvalidBase64CharactersDefect)。

decodeFalse(預設值)時,正文將作為字串返回,而無需解碼 Content-Transfer-Encoding。但是,對於 8bit 的 Content-Transfer-Encoding,會嘗試使用 Content-Type 標頭指定的 charsetreplace 錯誤處理程式來解碼原始位元組。如果沒有指定 charset,或者 email 包無法識別給定的 charset,則使用預設的 ASCII 字元集解碼正文。

這是一個遺留方法。在 EmailMessage 類上,其功能被 get_content()iter_parts() 所取代。

set_payload(payload, charset=None)

將整個訊息物件的有效負載設定為 payload。客戶端有責任確保有效負載的不變性。可選的 charset 設定訊息的預設字元集;有關詳細資訊,請參閱 set_charset()

這是一個遺留方法。在 EmailMessage 類中,其功能被 set_content() 替代。

set_charset(charset)

將有效負載的字元集設定為 charset,它可以是 Charset 例項(請參閱 email.charset),一個命名字元集的字串,或 None。 如果它是一個字串,它將被轉換為一個 Charset 例項。 如果 charsetNone,則 charset 引數將從 Content-Type 標頭中移除(訊息不會以其他方式修改)。 任何其他情況都會生成一個 TypeError

如果不存在 MIME-Version 標頭,則會新增一個。 如果不存在 Content-Type 標頭,則會新增一個值為 text/plain 的標頭。 無論 Content-Type 標頭是否已存在,其 charset 引數都將設定為 charset.output_charset。 如果 charset.input_charsetcharset.output_charset 不同,有效負載將重新編碼為 output_charset。 如果不存在 Content-Transfer-Encoding 標頭,則將使用指定的 Charset 對有效負載進行傳輸編碼(如果需要),並新增一個具有適當值的標頭。 如果 Content-Transfer-Encoding 標頭已存在,則假定有效負載已經使用該 Content-Transfer-Encoding 正確編碼,並且不會被修改。

這是一個遺留方法。在 EmailMessage 類中,其功能被 email.emailmessage.EmailMessage.set_content() 方法的 charset 引數替代。

get_charset()

返回與訊息的有效負載關聯的 Charset 例項。

這是一個遺留方法。在 EmailMessage 類中,它總是返回 None

以下方法實現了一個類似對映的介面,用於訪問訊息的 RFC 2822 標頭。 請注意,這些方法和普通對映(即字典)介面之間存在一些語義差異。 例如,在字典中沒有重複的鍵,但在這裡可能有重複的訊息標頭。 此外,在字典中,keys() 返回的鍵的順序沒有保證,但在 Message 物件中,標頭始終按照它們在原始訊息中出現的順序返回,或稍後新增到訊息中的順序返回。任何刪除然後重新新增的標頭始終附加到標頭列表的末尾。

這些語義差異是有意的,並且偏向於最大的便利性。

請注意,在所有情況下,訊息中存在的任何信封標頭都不包括在對映介面中。

在從位元組生成的模型中,任何(違反 RFC 的)包含非 ASCII 位元組的標頭值,透過此介面檢索時,將表示為字元集為 unknown-8bitHeader 物件。

__len__()

返回標頭的總數,包括重複項。

__contains__(name)

如果訊息物件具有名為 name 的欄位,則返回 True。 匹配不區分大小寫,並且 name 不應包含尾隨冒號。 用於 in 運算子,例如

if 'message-id' in myMessage:
   print('Message-ID:', myMessage['message-id'])
__getitem__(name)

返回指定標頭欄位的值。 name 不應包含冒號欄位分隔符。 如果缺少標頭,則返回 None;永遠不會引發 KeyError

請注意,如果指定欄位在訊息的標頭中多次出現,則返回哪個欄位值是不確定的。 使用 get_all() 方法獲取所有存在的指定標頭的值。

__setitem__(name, val)

向訊息新增一個標頭,其欄位名稱為 name,值為 val。 該欄位附加到訊息的現有欄位的末尾。

請注意,這不會覆蓋或刪除任何具有相同名稱的現有標頭。 如果您想確保新標頭是訊息中唯一具有欄位名稱 name 的標頭,請先刪除該欄位,例如

del msg['subject']
msg['subject'] = 'Python roolz!'
__delitem__(name)

從訊息的標頭中刪除名稱為 name 的欄位的所有例項。 如果標頭中不存在指定的欄位,則不會引發異常。

keys()

返回所有訊息的標頭欄位名稱的列表。

values()

返回所有訊息的欄位值的列表。

items()

返回包含所有訊息的欄位標頭和值的 2 元組列表。

get(name, failobj=None)

返回指定標頭欄位的值。 這與 __getitem__() 相同,只是如果缺少指定標頭,則返回可選的 failobj(預設為 None)。

以下是一些其他有用的方法

get_all(name, failobj=None)

返回名為 name 的欄位的所有值的列表。 如果訊息中沒有此類指定標頭,則返回 failobj(預設為 None)。

add_header(_name, _value, **_params)

擴充套件標頭設定。 此方法類似於 __setitem__(),只是可以作為關鍵字引數提供額外的標頭引數。 _name 是要新增的標頭欄位,_value 是標頭的主要值。

對於關鍵字引數字典 _params 中的每個條目,鍵會被視為引數名稱,其中下劃線會轉換為破折號(因為破折號在 Python 識別符號中是非法的)。通常,引數將以 key="value" 的形式新增,除非值是 None,在這種情況下,只會新增鍵。如果值包含非 ASCII 字元,則可以將其指定為三元組,格式為 (CHARSET, LANGUAGE, VALUE),其中 CHARSET 是一個字串,指定用於編碼值的字元集,LANGUAGE 通常可以設定為 None 或空字串(有關其他可能性,請參閱 RFC 2231),而 VALUE 是包含非 ASCII 程式碼點的字串值。如果沒有傳遞三元組且值包含非 ASCII 字元,則會自動以 RFC 2231 格式編碼,使用 utf-8CHARSETNoneLANGUAGE

這是一個示例

msg.add_header('Content-Disposition', 'attachment', filename='bud.gif')

這將新增一個看起來像這樣的標頭

Content-Disposition: attachment; filename="bud.gif"

一個包含非 ASCII 字元的示例

msg.add_header('Content-Disposition', 'attachment',
               filename=('iso-8859-1', '', 'Fußballer.ppt'))

這將產生

Content-Disposition: attachment; filename*="iso-8859-1''Fu%DFballer.ppt"
replace_header(_name, _value)

替換標頭。替換訊息中找到的第一個與 _name 匹配的標頭,保留標頭順序和欄位名稱大小寫。如果沒有找到匹配的標頭,則會引發 KeyError

get_content_type()

返回訊息的內容型別。返回的字串會被強制轉換為小寫的 maintype/subtype 格式。如果訊息中沒有 Content-Type 標頭,則會返回由 get_default_type() 給出的預設型別。根據 RFC 2045,訊息始終具有預設型別,因此 get_content_type() 將始終返回一個值。

RFC 2045 將訊息的預設型別定義為 text/plain,除非它出現在 multipart/digest 容器中,在這種情況下,它將是 message/rfc822。如果 Content-Type 標頭具有無效的型別規範,則 RFC 2045 強制規定預設型別為 text/plain

get_content_maintype()

返回訊息的主要內容型別。這是 get_content_type() 返回的字串的 maintype 部分。

get_content_subtype()

返回訊息的子內容型別。這是 get_content_type() 返回的字串的 subtype 部分。

get_default_type()

返回預設內容型別。大多數訊息的預設內容型別為 text/plain,除了 multipart/digest 容器的子部分訊息。這些子部分的預設內容型別為 message/rfc822

set_default_type(ctype)

設定預設內容型別。ctype 應該是 text/plainmessage/rfc822,儘管這不是強制性的。預設內容型別不會儲存在 Content-Type 標頭中。

get_params(failobj=None, header='content-type', unquote=True)

以列表形式返回訊息的 Content-Type 引數。返回的列表元素是鍵/值對的二元組,以 '=' 符號分割。 '=' 的左側是鍵,右側是值。如果引數中沒有 '=' 符號,則值為空字串,否則該值如 get_param() 中所述,如果可選的 unquoteTrue(預設值),則會進行取消引用。

如果沒有 Content-Type 標頭,則可選的 failobj 是要返回的物件。可選的 header 是要搜尋的標頭,而不是 Content-Type

這是一個遺留方法。在 EmailMessage 類中,它的功能被標頭訪問方法返回的各個標頭物件的 params 屬性取代。

get_param(param, failobj=None, header='content-type', unquote=True)

以字串形式返回 Content-Type 標頭的引數 param 的值。如果訊息沒有 Content-Type 標頭或沒有這樣的引數,則返回 failobj(預設為 None)。

如果給定可選的 header,則指定要使用的訊息標頭,而不是 Content-Type

引數鍵始終以不區分大小寫的方式進行比較。返回值可以是字串,也可以是三元組,如果引數是 RFC 2231 編碼的。當它是三元組時,值的元素形式為 (CHARSET, LANGUAGE, VALUE)。請注意,CHARSETLANGUAGE 都可以是 None,在這種情況下,您應該認為 VALUE 是使用 us-ascii 字元集編碼的。通常可以忽略 LANGUAGE

如果您的應用程式不關心引數是否以 RFC 2231 中所述的方式編碼,您可以透過呼叫 email.utils.collapse_rfc2231_value() 來摺疊引數值,傳入從 get_param() 返回的值。當值是元組時,這將返回一個經過適當解碼的 Unicode 字串,或者如果它不是元組,則返回原始的未引用字串。例如

rawparam = msg.get_param('foo')
param = email.utils.collapse_rfc2231_value(rawparam)

在任何情況下,引數值(無論是返回的字串,還是三元組中的 VALUE 項)始終是不帶引號的,除非將 unquote 設定為 False

這是一個遺留方法。在 EmailMessage 類中,它的功能被標頭訪問方法返回的各個標頭物件的 params 屬性取代。

set_param(param, value, header='Content-Type', requote=True, charset=None, language='', replace=False)

Content-Type 頭部中設定一個引數。如果該引數已經存在於頭部中,則其值將被替換為 value。如果尚未為此訊息定義 Content-Type 頭部,則會將其設定為 text/plain,並且新的引數值將按照 RFC 2045 的規定附加。

可選的 header 指定一個替代的 Content-Type 頭部,並且所有引數都將被適當地引用,除非可選的 requoteFalse (預設為 True)。

如果指定了可選的 charset,則該引數將根據 RFC 2231 進行編碼。可選的 language 指定 RFC 2231 語言,預設為空字串。charsetlanguage 都應該是字串。

如果 replaceFalse (預設值),則頭部將被移動到頭部列表的末尾。如果 replaceTrue,則頭部將在原地更新。

在 3.4 版本中更改: 添加了 replace 關鍵字。

del_param(param, header='content-type', requote=True)

Content-Type 頭部中完全刪除給定的引數。頭部將在不包含該引數及其值的情況下就地重寫。所有值都將根據需要進行引用,除非 requoteFalse (預設為 True)。可選的 header 指定 Content-Type 的替代項。

set_type(type, header='Content-Type', requote=True)

設定 Content-Type 頭的主型別和子型別。type 必須是 maintype/subtype 形式的字串,否則會引發 ValueError

此方法替換 Content-Type 頭部,保留所有引數。如果 requoteFalse,則保持現有頭部的引用不變,否則將引用引數(預設值)。

可以在 header 引數中指定替代頭部。設定 Content-Type 頭部時,還會新增 MIME-Version 頭部。

這是一個遺留方法。在 EmailMessage 類中,其功能被 make_add_ 方法替代。

get_filename(failobj=None)

返回訊息的 Content-Disposition 頭部的 filename 引數的值。如果頭部沒有 filename 引數,此方法將回退到查詢 Content-Type 頭部上的 name 引數。如果兩者都找不到,或者頭部丟失,則返回 failobj。返回的字串將始終按照 email.utils.unquote() 的規定取消引用。

get_boundary(failobj=None)

返回訊息的 Content-Type 頭部的 boundary 引數的值,或者如果頭部丟失或沒有 boundary 引數,則返回 failobj。返回的字串將始終按照 email.utils.unquote() 的規定取消引用。

set_boundary(boundary)

Content-Type 頭部的 boundary 引數設定為 boundaryset_boundary() 將始終在必要時引用 boundary。如果訊息物件沒有 Content-Type 頭部,則會引發 HeaderParseError

請注意,使用此方法與刪除舊的 Content-Type 頭部,並透過 add_header() 新增一個具有新邊界的新頭部略有不同,因為 set_boundary() 保留了頭部列表中 Content-Type 頭部的順序。但是,它保留原始 Content-Type 頭部中可能存在的任何延續行。

get_content_charset(failobj=None)

返回 Content-Type 頭部的 charset 引數,並強制轉換為小寫。如果沒有 Content-Type 頭部,或者該頭部沒有 charset 引數,則返回 failobj

請注意,此方法與 get_charset() 不同,後者返回訊息正文的預設編碼的 Charset 例項。

get_charsets(failobj=None)

返回一個包含訊息中字元集名稱的列表。如果訊息是 multipart,則該列表將為有效負載中的每個子部分包含一個元素,否則,它將是一個長度為 1 的列表。

列表中的每個專案都將是一個字串,該字串是所表示的子部分的 Content-Type 頭部中 charset 引數的值。但是,如果子部分沒有 Content-Type 頭部,沒有 charset 引數,或者不是 text 主 MIME 型別,則返回列表中的該專案將為 failobj

get_content_disposition()

如果訊息具有 Content-Disposition 頭部,則返回該頭部的 小寫值(不帶引數),否則返回 None。此方法的可能值為 inlineattachmentNone(如果訊息遵循 RFC 2183)。

在 3.5 版本中新增。

walk()

walk() 方法是一個通用的生成器,可用於以深度優先遍歷順序迭代訊息物件樹的所有部分和子部分。通常,您會在 for 迴圈中使用 walk() 作為迭代器;每次迭代返回下一個子部分。

這是一個列印多部分訊息結構的每個部分的 MIME 型別的示例

>>> for part in msg.walk():
...     print(part.get_content_type())
multipart/report
text/plain
message/delivery-status
text/plain
text/plain
message/rfc822
text/plain

walk 遍歷任何部件的子部件,其中 is_multipart() 返回 True,即使 msg.get_content_maintype() == 'multipart' 可能返回 False。我們可以在示例中使用 _structure 除錯輔助函式來看到這一點。

>>> for part in msg.walk():
...     print(part.get_content_maintype() == 'multipart',
...           part.is_multipart())
True True
False False
False True
False False
False False
False True
False False
>>> _structure(msg)
multipart/report
    text/plain
    message/delivery-status
        text/plain
        text/plain
    message/rfc822
        text/plain

這裡,message 部件不是 multiparts,但它們確實包含子部件。is_multipart() 返回 True,並且 walk 會下降到子部件中。

Message 物件也可以選擇性地包含兩個例項屬性,這些屬性可以在生成 MIME 訊息的純文字時使用。

preamble

MIME 文件的格式允許在標頭之後的空行和第一個多部分邊界字串之間存在一些文字。通常,此文字在 MIME 感知的郵件閱讀器中永遠不可見,因為它超出了標準 MIME 外殼。但是,在檢視訊息的原始文字或在非 MIME 感知的閱讀器中檢視訊息時,此文字可能會變得可見。

preamble 屬性包含 MIME 文件的此引導額外外殼文字。當 Parser 在標頭之後但在第一個邊界字串之前發現一些文字時,它會將此文字分配給訊息的 preamble 屬性。當 Generator 正在寫出 MIME 訊息的純文字表示形式,並且它發現該訊息具有 preamble 屬性時,它會將此文字寫入標頭和第一個邊界之間的區域。有關詳細資訊,請參見 email.parseremail.generator

請注意,如果訊息物件沒有 preamble,則 preamble 屬性將為 None

epilogue

epilogue 屬性的作用與 preamble 屬性相同,只是它包含出現在最後一個邊界和訊息末尾之間的文字。

您無需將 epilogue 設定為空字串,以便 Generator 在檔案末尾列印換行符。

defects

defects 屬性包含解析此訊息時發現的所有問題的列表。有關可能的解析缺陷的詳細說明,請參見 email.errors