Python: So analysieren Sie den Body aus einer unformatierten E-Mail, da die rohe E-Mail kein "Body" -Tag oder ähnliches enthält

81

Es scheint leicht zu bekommen

From
To
Subject

usw. über

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

Angenommen, dies "a"ist die Raw-E-Mail-Zeichenfolge, die ungefähr so ​​aussieht.

a = """From [email protected] 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 <[email protected]>; 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: [email protected]
Subject: oooooooooooooooo
To: [email protected]
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--"""

DIE FRAGE

Wie kommt man Bodyvon dieser E-Mail über Python?

Bisher ist dies der einzige Code, den ich kenne, aber ich muss ihn noch testen.

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

Ist das der richtige Weg?

oder vielleicht gibt es etwas einfacheres wie ...

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

?

Codegeek
quelle

Antworten:

94

Verwenden Sie 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()
falsetru
quelle
111

Um sehr sicher zu sein, dass Sie mit dem eigentlichen E-Mail-Text arbeiten (und dennoch die Möglichkeit haben, dass Sie nicht den richtigen Teil analysieren), müssen Sie Anhänge überspringen und sich auf den einfachen oder HTML-Teil konzentrieren (je nach Ihren Anforderungen) wird bearbeitet.

Da die oben genannten Anhänge Text / Plain- oder Text / HTML-Teile enthalten können und sehr oft sind, überspringt dieses nicht kugelsichere Beispiel diese, indem es den Header für die Inhaltsdisposition überprüft:

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)

Übrigens, walk()iteriert wunderbar auf MIME-Teilen und get_payload(decode=True)erledigt die Drecksarbeit beim Decodieren von Base64 usw. für Sie.

Einige Hintergrundinformationen - wie ich bereits angedeutet habe, birgt die wunderbare Welt der MIME-E-Mails viele Fallstricke, wenn es darum geht, den Nachrichtentext "falsch" zu finden. Im einfachsten Fall befindet es sich im einzigen "Text / Plain" -Teil und get_payload () ist sehr verlockend, aber wir leben nicht in einer einfachen Welt - es ist oft von mehrteiligen / alternativen, verwandten, gemischten usw. Inhalten umgeben. Wikipedia beschreibt es genau - MIME , aber wenn man bedenkt, dass all diese Fälle gültig und häufig sind, muss man Sicherheitsnetze überall berücksichtigen:

Sehr häufig - so ziemlich das, was Sie im normalen Editor (Google Mail, Outlook) erhalten, wenn Sie formatierten Text mit einem Anhang senden:

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

Relativ einfach - nur alternative Darstellung:

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

Für gut oder schlecht ist diese Struktur auch gültig:

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

Hoffe das hilft ein bisschen.

PS Mein Punkt ist, dass Sie sich E-Mails nicht leichtfertig nähern - sie beißen, wenn Sie es am wenigsten erwarten :)

Todor Minakov
quelle
5
Vielen Dank für dieses gründliche Beispiel und für die Angabe einer Warnung - entgegen der akzeptierten Antwort. Ich denke, dies ist ein weitaus besserer / sicherer Ansatz.
Simon Steinberger
1
Ah, sehr gut! .get_payload(decode=True)anstatt nur .get_payload()das Leben viel einfacher gemacht zu haben, danke!
Mark
9

Es ist ein sehr gutes Paket verfügbar, um den E-Mail-Inhalt mit der richtigen Dokumentation zu analysieren.

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)

Wie benutzt man:

mail.attachments: list of all attachments
mail.body
mail.to
Amit Sharma
quelle
2
Die Bibliothek ist großartig, aber ich musste meine eigene Klasse erstellen , die von der Body- Methode erbt MailParserund diese überschreibt , da sie die Teile des E-Mail-Body mit "\ n --- mail_boundary --- \ n" verbindet, was für mich nicht ideal war.
Avram
hi @avram, könntest du bitte die Klasse teilen, die du geschrieben hast?
Amey P Naik
Ich habe es geschafft, das Ergebnis auf "\ n --- mail_boundary --- \ n" aufzuteilen.
Amey P Naik
1
@AmeyPNaik Hier habe ich einen kurzen Überblick gegeben: gist.github.com/aleksaa01/ccd371869f3a3c7b3e47822d5d78ccdf
avram
1
@AmeyPNaik in ihrer Dokumentation heißt es: Mail-Parser kann das Outlook-E-Mail-Format (.msg) analysieren. Um diese Funktion nutzen zu können, müssen Sie das Paket libemail-Outlook-Message-Perl
Ciprian Tomoiagă
6

Python 3.6+ bietet integrierte Komfortmethoden zum Suchen und Dekodieren des Nur-Text-Körpers wie in @Todor Minakovder Antwort. Sie können die Methoden EMailMessage.get_body()und get_content()verwenden:

msg = email.message_from_string(s, policy=email.policy.default)
body = msg.get_body(('plain',))
if body:
    body = body.get_content()
print(body)

Beachten Sie, dass dies gegeben ist, Nonewenn kein (offensichtlicher) Klartextkörperteil vorhanden ist.

Wenn Sie beispielsweise aus einer Mbox-Datei lesen, können Sie dem Postfachkonstruktor eine EmailMessageFactory geben:

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

Beachten Sie, dass Sie email.policy.defaultals Richtlinie übergeben müssen, da dies nicht die Standardeinstellung ist ...

Doktor J.
quelle
2
Warum ist nicht email.policy.defaultdie Standardeinstellung? Scheint so zu sein.
Teilbestellung
4

Es gibt keine b['body']in Python. Sie müssen get_payload verwenden.

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

Viel Glück.

Jimmy Lin
quelle
0

Wenn E-Mails der Pandas-Datenrahmen und E-Mails sind, geben Sie die Spalte für den E-Mail-Text an

## 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()
Ajay Ohri
quelle
-3

Hier ist der Code, der jedes Mal für mich funktioniert (für Outlook-E-Mails):

#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 ([email protected] is my root folder)

root_folder = 
outlook.Folders['[email protected]'].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
Deepesh Verma
quelle
4
Schreiben Sie vielleicht, dass dies für Outlook unter Windows gilt, nicht für echte E-Mails.
Tripleee