web-dev-qa-db-fra.com

Python: Comment analyser le corps d'un courrier électronique brut, étant donné que ce dernier n'a pas de balise "Body" ou quoi que ce soit

Il semble facile d’obtenir le

From
To
Subject

etc via

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

en admettant que "a" est la chaîne de courrier électronique brute qui ressemble à ceci.

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

LA QUESTION

comment obtenez-vous le Body de cet email via python?

Jusqu'à présent, c'est le seul code que je connaisse mais je n'ai pas encore testé.

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

est-ce la bonne façon ?

ou peut-être qu'il y a quelque chose de plus simple, comme ...

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

?

63
user2621078

Utilisez 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()
72
falsetru

Pour être très positif, vous travaillez avec le corps de l’e-mail (mais avec la possibilité de ne pas analyser la bonne partie), vous devez ignorer les pièces jointes et vous concentrer sur la partie simple ou html (selon vos besoins). En traitement.

Comme les pièces jointes susmentionnées peuvent et sont très souvent des parties text/plain ou text/html, cet exemple non protégé contre les balles les ignore en vérifiant l'en-tête 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)

BTW, walk() itère merveilleusement sur les parties mime, et get_payload(decode=True) effectue le travail compliqué sur le décodage en base64, etc. pour vous.

Un peu d’arrière-plan - comme je l’ai laissé entendre, le monde merveilleux des courriels MIME présente de nombreux pièges pour retrouver "à tort" le corps du message. Dans le cas le plus simple, il s'agit de la seule partie "text/plain" et get_payload () est très tentant, mais nous ne vivons pas dans un monde simple - il est souvent entouré de contenu multipart/alternatif, connexe, mixte, etc. Wikipedia le décrit très bien - MIME , mais compte tenu de tous les cas ci-dessous, ils sont valables - et communs -, il faut tenir compte des filets de sécurité:

Très commun - à peu près ce que vous obtenez dans l'éditeur normal (Gmail, Outlook) en envoyant du texte formaté avec une pièce jointe:

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

Relativement simple - juste une représentation alternative:

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

Pour le meilleur ou pour le pire, cette structure est également valide:

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

J'espère que ça aide un peu.

P.S. Mon problème est de ne pas approcher l'email à la légère - ça mord quand on s'y attend le moins :)

93
Todor Minakov

Il n'y a pas b['body'] en python. Vous devez utiliser 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...

Bonne chance.

4
Jimmy Lin

Il est très bon package disponible pour analyser le contenu de l'email avec la documentation appropriée.

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)

Comment utiliser:

mail.attachments: list of all attachments
mail.body
mail.to
2
Amit Sharma

Si emails est le pandas dataframe et emails.message la colonne pour le texte de l'e-mail

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