Python:生のメールに「Body」タグなどがない場合に、生のメールから本文を解析する方法


83

入手するのは簡単なようです

From
To
Subject

など経由

import email
b = email.message_from_string(a)
bbb = b['from']
ccc = b['to']

"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--"""

質問

BodyPython経由でこのメールをどのように入手しますか?

これまでのところ、これは私が知っている唯一のコードですが、まだテストしていません。

if email.is_multipart():
    for part in email.get_payload():
        print part.get_payload()
else:
    print email.get_payload()

これは正しい方法ですか?

または多分...のようなもっと簡単なものがあります

import email
b = email.message_from_string(a)
bbb = b['body']

回答:


95

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()

113

非常に前向きにするには、実際の電子メール本文を操作します(ただし、適切な部分を解析していない可能性があります)。添付ファイルをスキップし、プレーン部分または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)

ところで、walk()MIMEパーツで見事に反復し、get_payload(decode=True)し、base64などのデコードでダーティな作業を行います。

いくつかの背景-私が暗示したように、MIME電子メールの素晴らしい世界は、メッセージ本文を「間違って」見つけることの多くの落とし穴を提示します。最も単純なケースでは、それは唯一の「テキスト/プレーン」部分にあり、get_payload()は非常に魅力的ですが、私たちは単純な世界に住んでいません-それはしばしばマルチパート/代替、関連、混合などのコンテンツに囲まれています。ウィキペディアはそれをしっかりと説明しています-MIME、しかし、以下のすべてのこれらのケースが有効で検討-共通- 1は、すべての周りのセーフティネットを考慮することがあります。

非常に一般的です-添付ファイル付きのフォーマットされたテキストを送信する通常のエディター(Gmail、Outlook)で得られるものとほぼ同じです。

multipart/mixed
 |
 +- multipart/related
 |   |
 |   +- multipart/alternative
 |   |   |
 |   |   +- text/plain
 |   |   +- text/html
 |   |      
 |   +- image/png
 |
 +-- application/msexcel

比較的単純-単なる代替表現:

multipart/alternative
 |
 +- text/plain
 +- text/html

良くも悪くも、この構造は有効です。

multipart/alternative
 |
 +- text/plain
 +- multipart/related
      |
      +- text/html
      +- image/jpeg

これが少し役立つことを願っています。

PS私のポイントは、電子メールに軽くアプローチしないことです-あなたがそれを最も期待しないときにそれは噛みつきます:)


6
この徹底的な例と警告を綴ってくれてありがとう-受け入れられた答えとは反対に。これははるかに優れた/より安全なアプローチだと思います。
Simon Steinberger 2017年

1
ああ、とても良い!.get_payload(decode=True)ただの.get_payload()生活をずっと楽にしてくれたのではなく、ありがとう!
マーク

11

適切なドキュメントを使用して電子メールの内容を解析するために利用できる非常に優れたパッケージがあります。

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)

使い方:

mail.attachments: list of all attachments
mail.body
mail.to

2
ライブラリは素晴らしいですが、bodyメソッドを継承しMailParserてオーバーライドする独自のクラスを作成する必要がありました。これは、メールの本文の一部を「\ n --- mail_boundary --- \ n」結合するためです。これは私にとって理想的ではありませんでした。
avram 2018

こんにちは@avram、あなたが書いたクラスを共有していただけませんか?
Amey PNaik19年

結果を「\ n --- mail_boundary --- \ n」で分割することができました。
Amey PNaik19年

1
:@AmeyPNaikは、ここで私は、迅速なgithubのの要旨作らgist.github.com/aleksaa01/ccd37186​​9f3a3c7b3e47822d5d78ccdf
アヴラム

1
@AmeyPNaikのドキュメントには、次のように書かれています。mail -parserはOutlookの電子メール形式(.msg)を解析できます。この機能を使用するには、libemail-見通しメッセージ-perlのパッケージをインストールする必要があります
シプリアンTomoiagă

6

Python 3.6+には、@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)

これ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:
    ...

これはデフォルトではないemail.policy.defaultため、ポリシーとして渡す必要があることに注意してください...


2
なぜemail.policy.defaultデフォルトではないのですか?あるべきだと思われます。
PartialOrder

4

b['body']Pythonにはありません。get_payloadを使用する必要があります。

if isinstance(mailEntity.get_payload(), list):
    for eachPayload in mailEntity.get_payload():
        ...do things you want...
        ...real mail body is in eachPayload.get_payload()...
else:
    ...means there is only text/plain part....
    ...use mailEntity.get_payload() to get the body...

幸運を。


0

emailsがpandasデータフレームおよびemails.messageの場合、電子メールテキストの列

## Helper functions
def get_text_from_email(msg):
    '''To get the content from email objects'''
    parts = []
    for part in msg.walk():
        if part.get_content_type() == 'text/plain':
            parts.append( part.get_payload() )
    return ''.join(parts)

def split_email_addresses(line):
    '''To separate multiple email addresses'''
    if line:
        addrs = line.split(',')
        addrs = frozenset(map(lambda x: x.strip(), addrs))
    else:
        addrs = None
    return addrs 

import email
# Parse the emails into a list email objects
messages = list(map(email.message_from_string, emails['message']))
emails.drop('message', axis=1, inplace=True)
# Get fields from parsed email objects
keys = messages[0].keys()
for key in keys:
    emails[key] = [doc[key] for doc in messages]
# Parse content from emails
emails['content'] = list(map(get_text_from_email, messages))
# Split multiple email addresses
emails['From'] = emails['From'].map(split_email_addresses)
emails['To'] = emails['To'].map(split_email_addresses)

# Extract the root of 'file' as 'user'
emails['user'] = emails['file'].map(lambda x:x.split('/')[0])
del messages

emails.head()

-3

毎回(Outlookの電子メールの場合)機能するコードは次のとおりです。

#to read Subjects and Body of email in a folder (or subfolder)

import win32com.client  
#import package

outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")  
#create object

#get to the desired folder (MyEmail@xyz.com is my root folder)

root_folder = 
outlook.Folders['MyEmail@xyz.com'].Folders['Inbox'].Folders['SubFolderName']

#('Inbox' and 'SubFolderName' are the subfolders)

messages = root_folder.Items

for message in messages:
if message.Unread == True:    # gets only 'Unread' emails
    subject_content = message.subject
# to store subject lines of mails

    body_content = message.body
# to store Body of mails

    print(subject_content)
    print(body_content)

    message.Unread = True         # mark the mail as 'Read'
    message = messages.GetNext()  #iterate over mails

4
おそらく、これはWindows上のOutlook用であり、実際の電子メール用ではないことを説明します。
トリプリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.