web-dev-qa-db-fra.com

Comment puis-je obtenir le contenu texte d'un e-mail à l'aide de Python?

Étant donné un message RFC822 dans Python 2.6, comment puis-je obtenir la partie droite texte/contenu simple? l'algorithme que je veux est le suivant:

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

De ces choses, j'ai get_mime_part Et has_mime_part Vers le bas, mais je ne sais pas trop comment obtenir le texte décodé de la partie MIME. Je peux obtenir le texte encodé en utilisant get_payload(), mais si j'essaie d'utiliser le paramètre decode du get_payload() méthode (voir le doc ) J'obtiens une erreur quand je l'appelle sur la partie 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'>

De plus, je ne sais pas comment prendre du HTML et le restituer au texte le plus près possible.

36
Chris R

Dans un e-mail en plusieurs parties, email.message.Message.get_payload() renvoie une liste avec un élément pour chaque partie. Le moyen le plus simple est de parcourir le message et d'obtenir la charge utile sur chaque partie:

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

Pour un message non multipartite, pas besoin de faire toute la marche. Vous pouvez accéder directement à get_payload (), quel que soit le type de contenu.

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

Si le contenu est encodé, vous devez passer None comme premier paramètre à get_payload(), suivi de True (l'indicateur de décodage est le deuxième paramètre). Par exemple, supposons que mon e-mail contienne une pièce jointe au document 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()

Quant à obtenir une approximation raisonnable en texte brut d'une partie HTML, j'ai trouvé que html2text fonctionne très bien.

88
Jarret Hardie

L'appartement est meilleur que l'emboîtement;)

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 _
1
guneysus

Pour ajouter l'excellente réponse de @Jarret Hardie:

Personnellement, j'aime transformer ce type de structures de données en un dictionnaire que je pourrai réutiliser plus tard, donc quelque chose comme ça où le content_type est la clé et payload est la valeur:

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

0
Seraf