Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to receive mail using python

Tags:

python

email

I would like to receive email using python. So far I have been able to get the subject but not the body. Here is the code I have been using:

import poplib
from email import parser
pop_conn = poplib.POP3_SSL('pop.gmail.com')
pop_conn.user('myusername')
pop_conn.pass_('mypassword')
#Get messages from server:
messages = [pop_conn.retr(i) for i in range(1, len(pop_conn.list()[1]) + 1)]
# Concat message pieces:
messages = ["\n".join(mssg[1]) for mssg in messages]
#Parse message intom an email object:
messages = [parser.Parser().parsestr(mssg) for mssg in messages]
for message in messages:
    print message['subject']
    print message['body']
pop_conn.quit()

My issue is that when I run this code it properly returns the Subject but not the body. So if I send an email with the subject "Tester" and the body "This is a test message" it looks like this in IDLE.

>>>>Tester >>>>None

So it appears to be accurately assessing the subject but not the body, I think it is in the parsing method right? The issue is that I don't know enough about these libraries to figure out how to change it so that it returns both a subject and a body.

like image 879
Matthew Downey Avatar asked Feb 05 '11 17:02

Matthew Downey


2 Answers

Here is how I solved the problem using python 3 new capabilities:

import imaplib
import email

mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login(username, password)
mail.select(readonly=True)  # refresh inbox
status, message_ids = mail.search(None, 'ALL')  # get all emails
for message_id in message_ids[0].split():  # returns all message ids
    # for every id get the actual email
    status, message_data = mail.fetch(message_id, '(RFC822)')
    actual_message = email.message_from_bytes(message_data[0][1])

    # extract the needed fields
    email_date = actual_message["Date"]
    subject = actual_message["Subject"]
    message_body = get_message_body(actual_message)

Now get_message_body is actually pretty tricky due to MIME format. I used the function suggested in this answer.

This particular example works with Gmail, but IMAP is a standard protocol, so it should work for other email providers as well, possibly with minor changes.

like image 69
Peter K Avatar answered Oct 15 '22 17:10

Peter K


The object message does not have a body, you will need to parse the multiple parts, like this:

for part in message.walk():
    if part.get_content_type():
        body = part.get_payload(decode=True)

The walk() function iterates depth-first through the parts of the email, and you are looking for the parts that have a content-type. The content types can be either text/plain or text/html, and sometimes one e-mail can contain both (if the message content_type is set to multipart/alternative).

like image 41
Gustavo Costa De Oliveira Avatar answered Oct 15 '22 16:10

Gustavo Costa De Oliveira