How can I get an email message's text content using Python?
In a multipart e-mail, email.message.Message.get_payload()
returns a list with one item for each part. The easiest way is to walk the message and get the payload on each part:
import emailmsg = 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
For a non-multipart message, no need to do all the walking. You can go straight to get_payload(), regardless of content_type.
msg = email.message_from_string(raw_message)msg.get_payload()
If the content is encoded, you need to pass None
as the first parameter to get_payload()
, followed by True (the decode flag is the second parameter). For example, suppose that my e-mail contains an MS Word document attachment:
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()
As for getting a reasonable plain-text approximation of an HTML part, I've found that html2text works pretty darn well.
To add on @Jarret Hardie's excellent answer:
I personally like to transform that kind of data structures to a dictionary that I can reuse later, so something like this where the content_type
is the key and the payload
is the value:
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"])