46

给定 Python 2.6 中的 RFC822 消息,我怎样才能获得正确的文本/纯内容部分?基本上,我想要的算法是这样的:

message = email.message_from_string(raw_message)
if has_mime_part(message, "text/plain"):
    mime_part = get_mime_part(message, "text/plain")
    text_content = decode_mime_part(mime_part)
elif has_mime_part(message, "text/html"):
    mime_part = get_mime_part(message, "text/html")
    html = decode_mime_part(mime_part)
    text_content = render_html_to_plaintext(html)
else:
    # fallback
    text_content = str(message)
return text_content

在这些事情中,我已经get_mime_parthas_mime_part向下拍了拍,但我不太确定如何从 MIME 部分获取解码的文本。我可以使用 获取编码文本get_payload(),但是如果我尝试使用该方法的decode参数get_payload()(请参阅文档),当我在 text/plain 部分调用它时会出现错误:

File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/
email/message.py", line 189, in get_payload
    raise TypeError('Expected list, got %s' % type(self._payload))
TypeError: Expected list, got <type 'str'>

另外,我不知道如何获取 HTML 并将其呈现为尽可能接近的文本。

4

3 回答 3

104

在多部分电子邮件中,email.message.Message.get_payload()返回一个列表,其中每个部分都有一个项目。最简单的方法是遍历消息并获取每个部分的有效负载:

import email
msg = email.message_from_string(raw_message)
for part in msg.walk():
    # each part is a either non-multipart, or another multipart message
    # that contains further parts... Message is organized like a tree
    if part.get_content_type() == 'text/plain':
        print part.get_payload() # prints the raw text

对于非多部分消息,无需进行所有遍历。无论 content_type 是什么,您都可以直接进入 get_payload()。

msg = email.message_from_string(raw_message)
msg.get_payload()

如果内容被编码,则需要None作为第一个参数传递给get_payload(),然后是 True (解码标志是第二个参数)。例如,假设我的电子邮件包含一个 MS Word 文档附件:

msg = email.message_from_string(raw_message)
for part in msg.walk():
    if part.get_content_type() == 'application/msword':
        name = part.get_param('name') or 'MyDoc.doc'
        f = open(name, 'wb')
        f.write(part.get_payload(None, True)) # You need None as the first param
                                              # because part.is_multipart() 
                                              # is False
        f.close()

至于获得 HTML 部分的合理纯文本近似值,我发现html2text工作得非常好。

于 2009-09-22T23:17:31.193 回答
2

扁平比嵌套更好;)

from email.mime.multipart import MIMEMultipart
assert isinstance(msg, MIMEMultipart)

for _ in [k.get_payload() for k in msg.walk() if k.get_content_type() == 'text/plain']:
    print _
于 2015-12-02T10:06:28.697 回答
0

要添加@Jarret Hardie 的出色答案:

我个人喜欢将这种数据结构转换为以后可以重用的字典,所以像这样的地方content_type是键和payload值:

import email

[...]

email_message = {
    part.get_content_type(): part.get_payload()
    for part in email.message_from_bytes(raw_email).walk()
}

print(email_message["text/plain"])

于 2019-07-27T15:36:50.023 回答