Python:如果原始电子邮件没有"Body"标记或任何内容,如何从原始电子邮件解析Body

68 python email mod-wsgi wsgi python-2.7

这似乎很容易

From
To
Subject
Run Code Online (Sandbox Code Playgroud)

等等

import email
b = email.message_from_string(a)
bbb = b['from']
ccc = b['to']
Run Code Online (Sandbox Code Playgroud)

假设这"a"是原始电子邮件字符串,看起来像这样.

a = """From root@a1.local.tld Thu Jul 25 19:28:59 2013
Received: from a1.local.tld (localhost [127.0.0.1])
    by a1.local.tld (8.14.4/8.14.4) with ESMTP id r6Q2SxeQ003866
    for <ooo@a1.local.tld>; Thu, 25 Jul 2013 19:28:59 -0700
Received: (from root@localhost)
    by a1.local.tld (8.14.4/8.14.4/Submit) id r6Q2Sxbh003865;
    Thu, 25 Jul 2013 19:28:59 -0700
From: root@a1.local.tld
Subject: oooooooooooooooo
To: ooo@a1.local.tld
Cc: 
X-Originating-IP: 192.168.15.127
X-Mailer: Webmin 1.420
Message-Id: <1374805739.3861@a1>
Date: Thu, 25 Jul 2013 19:28:59 -0700 (PDT)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="bound1374805739"

This is a multi-part message in MIME format.

--bound1374805739
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

ooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooo

--bound1374805739--"""
Run Code Online (Sandbox Code Playgroud)

问题

你怎么Body通过python 获得这封电子邮件?

到目前为止,这是我所知道的唯一代码,但我还没有测试它.

if email.is_multipart():
    for part in email.get_payload():
        print part.get_payload()
else:
    print email.get_payload()
Run Code Online (Sandbox Code Playgroud)

这是正确的方法吗?

或者可能有更简单的东西,比如......

import email
b = email.message_from_string(a)
bbb = b['body']
Run Code Online (Sandbox Code Playgroud)

Tod*_*kov 96

为了高度肯定你使用实际的电子邮件正文(但仍然有可能你没有解析正确的部分),你必须跳过附件,并专注于普通或HTML部分(根据您的需要)进一步处理.

由于前面提到的附件可以并且经常是text/plain或text/html部分,因此这个非防弹样本通过检查content-disposition头来跳过这些:

b = email.message_from_string(a)
body = ""

if b.is_multipart():
    for part in b.walk():
        ctype = part.get_content_type()
        cdispo = str(part.get('Content-Disposition'))

        # skip any text/plain (txt) attachments
        if ctype == 'text/plain' and 'attachment' not in cdispo:
            body = part.get_payload(decode=True)  # decode
            break
# not multipart - i.e. plain text, no attachments, keeping fingers crossed
else:
    body = b.get_payload(decode=True)
Run Code Online (Sandbox Code Playgroud)

顺便说一句,walk()在mime部分上进行了非常好的迭代,并get_payload(decode=True)为你解决base64等问题.

一些背景 - 正如我暗示的那样,MIME电子邮件的精彩世界带来了许多"错误地"找到邮件正文的陷阱.在最简单的情况下,它位于唯一的"text/plain"部分,而get_payload()非常诱人,但我们并不生活在一个简单的世界中 - 它通常包含在多部分/替代,相关,混合等内容中.维基百科对它进行了严格的描述 - MIME,但考虑到以下所有这些情况都是有效的 - 而且很常见 - 人们必须考虑安全网:

很常见 - 几乎是你在普通编辑器(Gmail,Outlook)中发送带有附件的格式化文本:

multipart/mixed
 |
 +- multipart/related
 |   |
 |   +- multipart/alternative
 |   |   |
 |   |   +- text/plain
 |   |   +- text/html
 |   |      
 |   +- image/png
 |
 +-- application/msexcel
Run Code Online (Sandbox Code Playgroud)

相对简单 - 只是替代表示:

multipart/alternative
 |
 +- text/plain
 +- text/html
Run Code Online (Sandbox Code Playgroud)

无论好坏,这种结构也是有效的:

multipart/alternative
 |
 +- text/plain
 +- multipart/related
      |
      +- text/html
      +- image/jpeg
Run Code Online (Sandbox Code Playgroud)

希望这个对你有帮助.

PS我的观点是不要轻易接近电子邮件 - 当你最不期望它时它会咬人:)

  • 感谢这个彻底的例子和拼写出警告 - 与接受的答案相反.我认为这是一种更好/更安全的方法. (5认同)
  • 啊,很好!`.get_payload(decode=True)` 而不仅仅是 `.get_payload()` 让生活变得更轻松,谢谢! (2认同)

fal*_*tru 74

使用Message.get_payload

b = email.message_from_string(a)
if b.is_multipart():
    for payload in b.get_payload():
        # if payload.is_multipart(): ...
        print payload.get_payload()
else:
    print b.get_payload()
Run Code Online (Sandbox Code Playgroud)

  • 其他答案在更加健壮和利用更新的 get_body() 功能方面做得更好。 (3认同)
  • @nealmcb,当我回答时,没有 `get_body` ;)似乎它是从 Python 3.6 开始就出现的。顺便说一句,这个问题被标记为“python-2.7”,你不能使用“get_body” (3认同)
  • 好点子!当然,随着 Python 2 的生命周期结束一年多,我们可以对现代解决方案产生更多的兴趣。但还要注意,正如托多尔所描述的,许多电子邮件都有棘手的结构,因此更通用的方法是一个好主意,并且您的“...”不是很具体。 (2认同)

Doc*_*r J 10

Python 3.6+ 提供了内置的便捷方法来查找和解码纯文本正文,如 in@Todor Minakov的答案。您可以使用EMailMessage.get_body()get_content()方法:

msg = email.message_from_string(s, policy=email.policy.default)
body = msg.get_body(('plain',))
if body:
    body = body.get_content()
print(body)
Run Code Online (Sandbox Code Playgroud)

请注意,None如果没有(明显的)纯文本正文部分,这将给出。

如果您正在读取例如 mbox 文件,您可以为邮箱构造函数提供一个EmailMessage工厂:

mbox = mailbox.mbox(mboxfile, factory=lambda f: email.message_from_binary_file(f, policy=email.policy.default), create=False)
for msg in mbox:
    ...
Run Code Online (Sandbox Code Playgroud)

请注意,您必须email.policy.default作为策略传递,因为它不是默认值...

  • 为什么 `email.policy.default` 不是默认值?看起来应该是这样。 (2认同)

Ami*_*rma 5

有一个非常好的软件包,可以使用适当的文档来解析电子邮件内容。

import mailparser

mail = mailparser.parse_from_file(f)
mail = mailparser.parse_from_file_obj(fp)
mail = mailparser.parse_from_string(raw_mail)
mail = mailparser.parse_from_bytes(byte_mail)
Run Code Online (Sandbox Code Playgroud)

如何使用:

mail.attachments: list of all attachments
mail.body
mail.to
Run Code Online (Sandbox Code Playgroud)

  • @AmeyPNaik在这里我做了一个快速的github要点:https://gist.github.com/aleksaa01/ccd37186​​9f3a3c7b3e47822d5d78ccdf (3认同)
  • 库很棒,但我必须创建自己的类,该类继承自 `MailParser` 并覆盖 **body** 方法,因为它将电子邮件正文的各个部分与 **" 连接起来\n--- mail_boundary ---\n “**这对我来说并不理想。 (2认同)