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_ 標頭。 載荷 對于簡單消息對象的情況是一個字符串或字節(jié)串,對于 MIME 容器文檔的情況 (例如 multipart/*message/rfc822) 則是一個 Message 對象。

以下是 Message 類的方法:

class email.message.Message(policy=compat32)?

如果指定了 policy (它必須為 policy 類的實例) 則使用它所設置的規(guī)則來更新和序列化消息的表示形式。 如果未設置 policy,則使用 compat32 策略,該策略會保持對 Python 3.2 版 email 包的向下兼容性。 更多信息請參閱 policy 文檔。

在 3.3 版更改: 增加了 policy 關鍵字參數(shù)。

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

以展平的字符串形式返回整個消息對象。 或可選的 unixfrom 為真值,返回的字符串會包括封包標頭。 unixfrom 的默認值是 False。 出于保持向下兼容性的原因,maxheaderlen 的默認值是 0,因此如果你想要不同的值你必須顯式地重載它(在策略中為 max_line_length 指定的值將被此方法忽略)。 policy 參數(shù)可被用于覆蓋從消息實例獲取的默認策略。 這可以用來對該方法所輸出的格式進行一些控制,因為指定的 policy 將被傳遞給 Generator。

如果需要填充默認值以完成對字符串的轉換則展平消息可能觸發(fā)對 Message 的修改(例如,MIME 邊界可能會被生成或被修改)。

請注意此方法是出于便捷原因提供的,可能無法總是以你想要的方式格式化消息。 例如,在默認情況下它不會按 unix mbox 格式的要求對以 From 打頭的行執(zhí)行調整。 為了獲得更高靈活性,請實例化一個 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 標準進行編碼的二進制數(shù)據(jù),則這些不合規(guī)數(shù)據(jù)將被 unicode "unknown character" 碼位值所替代。 (另請參閱 as_bytes()BytesGenerator。)

在 3.4 版更改: 增加了 policy 關鍵字參數(shù)。

__str__()?

as_string() 等價。 這將讓 str(msg) 產生一個包含已格式化消息的字符號。

as_bytes(unixfrom=False, policy=None)?

以字節(jié)串對象的形式返回整個扁平化后的消息。 當可選的 unixfrom 為真值時,返回的字符串會包括封包標頭。 unixfrom 的默認值為 False。 policy 參數(shù)可被用于重載從消息實例獲取的默認策略。 這可被用來控制該方法所產生的部分格式化效果,因為指定的 policy 將被傳遞給 BytesGenerator

如果需要填充默認值以完成對字符串的轉換則展平消息可能觸發(fā)對 Message 的修改(例如,MIME 邊界可能會被生成或被修改)。

請注意此方法是出于便捷原因提供的,可能無法總是以你想要的方式格式化消息。 例如,在默認情況下它不會按 unix mbox 格式的要求對以 From 打頭的行執(zhí)行調整。 為了獲得更高靈活性,請實例化一個 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) 產生一個包含已格式化消息的字節(jié)串對象。

3.4 新版功能.

is_multipart()?

如果該消息的載荷是一個子 Message 對象列表則返回 True,否則返回 False。 當 is_multipart() 返回 False 時,載荷應當是一個字符串對象(有可能是一個 CTE 編碼的二進制載荷)。 (請注意 is_multipart() 返回 True 并不意味著 "msg.get_content_maintype() == 'multipart'" 將返回 True。 例如,is_multipartMessage 類型為 message/rfc822 時也將返回 True。)

set_unixfrom(unixfrom)?

將消息的封包標頭設為 unixfrom,這應當是一個字符串。

get_unixfrom()?

返回消息的信封頭。如果信封頭從未被設置過,默認返回 None

attach(payload)?

將給定的 payload 添加到當前載荷中,當前載荷在該調用之前必須為 None 或是一個 Message 對象列表。 在調用之后,此載荷將總是一個 Message 對象列表。 如果你想將此載荷設為一個標量對象(如字符串),請改用 set_payload()。

這是一個過時的方法。 在 EmailMessage 類上它的功能已被 set_content() 及相應的 makeadd 方法所替代。

get_payload(i=None, decode=False)?

返回當前的載荷,它在 is_multipart()True 時將是一個 Message 對象列表,在 is_multipart()False 時則是一個字符串。 如果該載荷是一個列表且你修改了這個列表對象,那么你就是原地修改了消息的載荷。

傳入可選參數(shù) i 時,如果 is_multipart()True,get_payload() 將返回載荷從零開始計數(shù)的第 i 個元素。 如果 i 小于 0 或大于等于載荷中的條目數(shù)則將引發(fā) IndexError。 如果載荷是一個字符串 (即 is_multipart()False) 且給出了 i,則會引發(fā) TypeError。

可選的 decode 是一個指明載荷是否應根據(jù) Content-Transfer-Encoding 標頭被解碼的旗標。 當其值為 True 且消息沒有多個部分時,如果此標頭值為 quoted-printablebase64 則載荷將被解碼。 如果使用了其他編碼格式,或者找不到 Content-Transfer-Encoding 標頭時,載荷將被原樣返回(不編碼)。 在所有情況下返回值都是二進制數(shù)據(jù)。 如果消息有多個部分且 decode 旗標為 True,則將返回 None。 如果載荷為 base64 但內容不完全正確(如缺少填充符、存在 base64 字母表以外的字符等),則將在消息的缺陷屬性中添加適當?shù)娜毕葜?(分別為 InvalidBase64PaddingDefectInvalidBase64CharactersDefect)。

decodeFalse (默認值) 時消息體會作為字符串返回而不解碼 Content-Transfer-Encoding。 但是,對于 Content-Transfer-Encoding 為 8bit 的情況,會嘗試使用 Content-Type 標頭指定的 charset 來解碼原始字節(jié)串,并使用 replace 錯誤處理程序。 如果未指定 charset,或者如果指定的 charset 未被 email 包所識別,則會使用默認的 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()?

返回與消息的載荷相關聯(lián)的 Charset 實例。

這是一個過時的方法。 在 EmailMessage 類上它將總是返回 None

以下方法實現(xiàn)了用于訪問消息的 RFC 2822 標頭的類映射接口。 請注意這些方法和普通映射(例如字典)接口之間存在一些語義上的不同。 舉例來說,在一個字典中不能有重復的鍵,但消息標頭則可能有重復。 并且,在字典中由 keys() 返回的鍵的順序是沒有保證的,但在 Message 對象中,標頭總是會按它們在原始消息中的出現(xiàn)或后繼加入順序返回。 任何已刪除再重新加入的標頭總是會添加到標頭列表的末尾。

這些語義上的差異是有意為之且其目的是為了提供最大的便利性。

請注意在任何情況下,消息當中的任何封包標頭都不會包含在映射接口當中。

在由字節(jié)串生成的模型中,任何包含非 ASCII 字節(jié)數(shù)據(jù)(違反 RFC)的標頭值當通過此接口來獲取時,將被表示為使用 unknown-8bit 字符集的 Header 對象。

__len__()?

返回標頭的總數(shù),包括重復項。

__contains__(name)?

如果消息對象中有一個名為 name 的字段則返回 True。 匹配操作對大小寫不敏感并且 name 不應包括末尾的冒號。 用于 in 運算符,例如:

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

返回指定名稱標頭字段的值。 name 不應包括作為字段分隔符的冒號。 如果標頭未找到,則返回 NoneKeyError 永遠不會被引發(fā)。

請注意如果指定名稱的字段在消息標頭中多次出現(xiàn),具體將返回哪個字段值是未定義的。 請使用 get_all() 方法來獲取所有指定名稱標頭的值。

__setitem__(name, val)?

將具有字段名 name 和值 val 的標頭添加到消息中。 字段會被添加到消息的現(xiàn)有字段的末尾。

請注意,這個方法 既不會 覆蓋 也不會 刪除任何字段名重名的已有字段。如果你確實想保證新字段是整個信息頭當中唯一擁有 name 字段名的字段,你需要先把舊字段刪除。例如:

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

刪除信息頭當中字段名匹配 name 的所有字段。如果匹配指定名稱的字段沒有找到,也不會拋出任何異常。

keys()?

以列表形式返回消息頭中所有的字段名。

values()?

以列表形式返回消息頭中所有的字段值。

items()?

以二元元組的列表形式返回消息頭中所有的字段名和字段值。

get(name, failobj=None)?

返回指定名稱標頭字段的值。 這與 __getitem__() 是一樣的,不同之處在于如果指定名稱標頭未找到則會返回可選的 failobj (默認為 None)。

以下是一些有用的附加方法:

get_all(name, failobj=None)?

返回字段名為 name 的所有字段值的列表。如果信息內不存在匹配的字段,返回 failobj (其默認值為 None )。

add_header(_name, _value, **_params)?

高級頭字段設定。這個方法與 __setitem__() 類似,不過你可以使用關鍵字參數(shù)為字段提供附加參數(shù)。 _name 是字段名, _value 是字段 值。

對于關鍵字參數(shù)字典 _params 中的每一項,其鍵會被當作形參名,并執(zhí)行下劃線和連字符間的轉換(因為連字符不是合法的 Python 標識符)。 通常,形參將以 key="value" 的形式添加,除非值為 None,在這種情況下將只添加鍵。 如果值包含非 ASCII 字符,可將其指定為格式為 (CHARSET, LANGUAGE, VALUE) 的三元組,其中 CHARSET 為要用來編碼值的字符集名稱字符串,LANGUAGE 通??稍O為 None 或空字符串(請參閱 RFC 2231 了解其他可能的取值),而 VALUE 為包含非 ASCII 碼位的字符串值。 如果不是傳入一個三元組且值包含非 ASCII 字符,則會自動以 RFC 2231 格式使用 CHARSETutf-8LANGUAGENone 對其進行編碼。

以下是為示例代碼:

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 的消息中找到的第一個標頭,標頭順序和字段名大小寫保持不變。 如果未找到匹配的標頭,則會引發(fā) KeyError。

get_content_type()?

返回消息的內容類型。 返回的字符串會強制轉換為 maintype/subtype 的全小寫形式。 如果消息中沒有 Content-Type 標頭則將返回由 get_default_type() 給出的默認類型。 因為根據(jù) RFC 2045,消息總是要有一個默認類型,所以 get_content_type() 將總是返回一個值。

RFC 2045 將消息的默認類型定義為 text/plain,除非它是出現(xiàn)在 multipart/digest 容器內,在這種情況下其類型應為 message/rfc822。 如果 Content-Type 標頭指定了無效的類型,RFC 2045 規(guī)定其默認類型應為 text/plain。

get_content_maintype()?

返回信息的主要內容類型。準確來說,此方法返回的是 get_content_type() 方法所返回的形如 maintype/subtype 的字符串當中的 maintype 部分。

get_content_subtype()?

返回信息的子內容類型。準確來說,此方法返回的是 get_content_type() 方法所返回的形如 maintype/subtype 的字符串當中的 subtype 部分。

get_default_type()?

返回默認的內容類型。絕大多數(shù)的信息,其默認內容類型都是 text/plain 。作為 multipart/digest 容器內子部分的信息除外,它們的默認內容類型是 message/rfc822

set_default_type(ctype)?

設置默認的內容類型。 ctype 應當為 text/plain 或者 message/rfc822,盡管這并非強制。 默認的內容類型不會存儲在 Content-Type 標頭中。

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

將消息的 Content-Type 形參作為列表返回。 所返回列表的元素為以 '=' 號拆分出的鍵/值對 2 元組。 '=' 左側的為鍵,右側的為值。 如果形參值中沒有 '=' 號,否則該將值如 get_param() 描述并且在可選 unquoteTrue (默認值) 時會被取消轉義。

可選的 failobj 是在沒有 Content-Type 標頭時要返回的對象。 可選的 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 編碼則是一個 3 元組。 當為 3 元組時,值中的元素采用 (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)

無論在哪種情況下,形參值(或為返回的字符串,或為 3 元組形式的 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 的替代標頭,并且所有形參將根據(jù)需要被轉換,除非可選的 requoteFalse (默認為 True)。

如果指定了可選的 charset,形參將按照 RFC 2231 來編碼。 可選的 language 指定了 RFC 2231 的語言,默認為空字符串。 charsetlanguage 都應為字符串。

如果 replaceFalse (默認值),該頭字段會被移動到所有頭字段列表的末尾。如果 replaceTrue ,字段會被原地更新。

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

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

Content-Type 標頭中完全移除給定的形參。 標頭將被原地重寫并不帶該形參或它的值。 所有的值將根據(jù)需要被轉換,除非 requoteFalse (默認為 True)。 可選的 header 指定 Content-Type 的一個替代項。

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

設置 Content-Type 標頭的主類型和子類型。 type 必須為 maintype/subtype 形式的字符串,否則會引發(fā) ValueError。

此方法可替換 Content-Type 標頭,并保持所有形參不變。 如果 requoteFalse,這會保持原有標頭引用轉換不變,否則形參將被引用轉換(默認行為)。

可以在 header 參數(shù)中指定一個替代標頭。 當 Content-Type 標頭被設置時也會添加一個 MIME-Version 標頭。

這是一個過時的方法。 在 EmailMessage 類上它的功能已被 make_add_ 方法所替代。

get_filename(failobj=None)?

返回信息頭當中 Content-Disposition 字段當中名為 filename 的參數(shù)值。如果該字段當中沒有此參數(shù),該方法會退而尋找 Content-Type 字段當中的 name 參數(shù)值。如果這個也沒有找到,或者這些個字段壓根就不存在,返回 failobj 。返回的字符串永遠按照 email.utils.unquote() 方法去除引號。

get_boundary(failobj=None)?

返回信息頭當中 Content-Type 字段當中名為 boundary 的參數(shù)值。如果字段當中沒有此參數(shù),或者這些個字段壓根就不存在,返回 failobj 。返回的字符串永遠按照 email.utils.unquote() 方法去除引號。

set_boundary(boundary)?

Content-Type 頭字段的 boundary 參數(shù)設置為 boundary 。 set_boundary() 方法永遠都會在必要的時候為 boundary 添加引號。如果信息對象中沒有 Content-Type 頭字段,拋出 HeaderParseError 異常。

請注意使用這個方法與刪除舊的 Content-Type 標頭并通過 add_header() 添加一個帶有新邊界的新標頭有細微的差異,因為 set_boundary() 會保留 Content-Type 標頭在原標頭列表中的順序。 但是,它 不會 保留原 Content-Type 標頭中可能存在的任何連續(xù)的行。

get_content_charset(failobj=None)?

返回 Content-Type 頭字段中的 charset 參數(shù),強制小寫。如果字段當中沒有此參數(shù),或者這個字段壓根不存在,返回 failobj 。

請注意此方法不同于 get_charset(),后者會返回 Charset 實例作為消息體的默認編碼格式。

get_charsets(failobj=None)?

返回一個包含了信息內所有字符集名字的列表。 如果信息是 multipart 類型的,那么列表當中的每一項都對應其載荷的子部分的字符集名字。 否則,該列表是一個長度為 1 的列表。

列表中的每一項都是字符串,它們是其所表示的子部分的 Content-Type 標頭中 charset 形參的值。 但是,如果該子部分沒有 Content-Type 標頭,或沒有 charset 形參,或者主 MIME 類型不是 text,則所返回列表中的對應項將為 failobj。

get_content_disposition()?

如果信息的 Content-Disposition 頭字段存在,返回其字段值;否則返回 None 。返回的值均為小寫,不包含參數(shù)。如果信息遵循 RFC 2183 標準,則此方法的返回值只可能在 inline 、 attachmentNone 之間選擇。

3.5 新版功能.

walk()?

walk() 方法是一個多功能生成器。它可以被用來以深度優(yōu)先順序遍歷信息對象樹的所有部分和子部分。一般而言, walk() 會被用作 for 循環(huán)的迭代器,每一次迭代都返回其下一個子部分。

以下例子會打印出一封具有多部分結構之信息的每個部分的 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 除錯幫助函數(shù)可以幫助我們在下面這個例子當中看清楚這一點:

>>>
>>> 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 在標頭之后及第一個分界字符串之前發(fā)現(xiàn)一些文本時,它會將這些文本賦值給消息的 preamble 屬性。 當 Generator 寫出 MIME 消息的純文本表示形式時,如果它發(fā)現(xiàn)消息具有 preamble 屬性,它將在標頭及第一個分界之間區(qū)域寫出這些文本。 請參閱 email.parseremail.generator 了解更多細節(jié)。

請注意如果消息對象沒有前導文本,則 preamble 屬性將為 None

epilogue?

epilogue 屬性的作用方式與 preamble 屬性相同,區(qū)別在于它包含出現(xiàn)于最后一個分界與消息結尾之間的文本。

你不需要將 epilogue 設為空字符串以便讓 Generator 在文件末尾打印一個換行符。

defects?

defects 屬性包含在解析消息時發(fā)現(xiàn)的所有問題的列表。 請參閱 email.errors 了解可能的解析缺陷的詳細描述。