How do I print multiple email bodies with python imap?
Asked Answered
W

2

1

I'm very new at coding and this is my first real attempt to do anything like this. I want to print out multiple email bodies, that are at least two weeks old. (later on in my finished program, i want to make this changeable)

I've looked around other similar post but i just couldn't get it to work. The code i've posted is how far i've come at the moment. I can print out the latest email just fine, but how do i go on from this?

import imaplib
import sys


mail = imaplib.IMAP4_SSL('Server')
mail.login('User', 'Password')

mail.select('Inbox')
result, data = mail.uid('search', None, 'ALL')
ids = data[0]
id_list = ids.split()
latest_email_uid = int(id_list[-1])

for i in range(latest_email_uid, latest_email_uid-5, -1):
    result, data = mail.uid('fetch', i, '(RFC822)')
raw_email = data[0][1]

import email

email_message = email.message_from_string(raw_email)

print email_message.get_payload(decode = True)

def get_first_text_block(self, email_message_instance):
    maintype = email_message_instance.get_content_maintype()
    if maintype == 'multipart':
       for part in email_message_instance.get_payload():
        if part.get_content_maintype() == 'text':
            return part.get_payload
elif maintype == "text":
    return email_message_instance.get_payload()

Thank you!

UPDATE New code!

import imaplib
import sys
import email

mail = imaplib.IMAP4_SSL('server')
mail.login('user', 'password')


mail.select('Inbox')
result, data = mail.uid('search', None, 'ALL')

ids = data[0]
id_list = ids.split()
latest_email_uid = int(id_list[-1])

raw_emails = []
for i in range(latest_email_uid, latest_email_uid-5, -1):
   result, data = mail.uid('fetch', i, '(RFC822)')
   raw_emails.append(data[0][1])

for raw_email in raw_emails:
        email_message = email.message_from_string(raw_email)


print email_message.get_payload(decode = True)

def get_first_text_block(self, email_message_instance):
    maintype = email_message_instance.get_content_maintype()
    if maintype == 'multipart':
        for part in email_message_instance.get_payload():
            if part.get_content_maintype() == 'text':
                return part.get_payload
    elif maintype == "text":
        return email_message_instance.get_payload()

This is how it looks, i probably made some mistake which results in only one email body is printed. Help is much appreciated!

Walsh answered 28/2, 2014 at 10:32 Comment(1)
A side problem: The 'ALL' probably should be 'BEFORE 16-feb-2014' or so, if you really want to act only on two-week-old mail.Wiltshire
P
1

I think your problem is here:

for i in range(latest_email_uid, latest_email_uid-5, -1):
    result, data = mail.uid('fetch', i, '(RFC822)')
raw_email = data[0][1]

After the loop, data only contains the last email you iterate over. Instead, get a list of emails:

raw_emails = []
for i in range(latest_email_uid, latest_email_uid-5, -1):
    result, data = mail.uid('fetch', i, '(RFC822)')
    raw_emails.append(data[0][1])

You can now iterate over those:

for raw_email in raw_emails:
    email_message = email.message_from_string(raw_email)
    ...

(Note: you should follow PEP-0008 and put all import statements at the top - it makes it easier to read and understand the code, particularly as it grows.)


Edit:

Your revision only prints once because you print after the for loop runs, not for each iteration. Indentation is important:

for raw_email in raw_emails:
    email_message = email.message_from_string(raw_email)
    print email_message.get_payload(decode=True) # note indentation
Petcock answered 28/2, 2014 at 10:43 Comment(4)
it still only prints out the first email body.. but thank you anyway!Walsh
@Walsh I have edited my answer; you basically had exactly the same problem again.Petcock
@jonrsharpe: wops..my bad, sorry! When i fixed it i got "TypeError: 'NoneType' object has no attribute 'getitem'" on line 19, raw_emails.append(data[0][1])Walsh
Then either data is None or data[0] is NonePetcock
M
1

@jonrsharpe pointed out already the indentation error in your code.

Here's Python 3 script that you could adapt for Python 2 that prints emails from a given time period:

#!/usr/bin/env python3
"""Print emails from a given date interval.

Usage:

    $ %(prog)s <since_date> <before_date>

since_date < before_date

Date format: DD-Mon-YYYY e.g., 3-Mar-2014

Based on http://pymotw.com/2/imaplib/
"""
import email
import sys
from datetime import datetime
from imaplib import IMAP4_SSL

import imaplib; imaplib.Debug = True

def decode_header(header_string):
    try:
        decoded_seq = email.header.decode_header(header_string)
        return str(email.header.make_header(decoded_seq))
    except Exception: # fallback: return as is
        return header_string

def get_text(msg, fallback_encoding='utf-8', errors='replace'):
    """Extract plain text from email."""
    text = []
    for part in msg.walk():
        if part.get_content_type() == 'text/plain':
            p = part.get_payload(decode=True)
            if p is not None:
                text.append(p.decode(part.get_charset() or fallback_encoding,
                                     errors))
    return "\n".join(text)


# define since/before dates
date_format = "%d-%b-%Y" # DD-Mon-YYYY e.g., 3-Mar-2014
since_date = datetime.strptime(sys.argv[1], date_format)
before_date = datetime.strptime(sys.argv[2], date_format)

imap_host, imap_port = "imap.gmail.com", 993
login, password = '[email protected]', 'password'

# connect to the imap server
mail = IMAP4_SSL(imap_host, imap_port)
mail.login(login, password)
try:
    mail.select('INBOX', readonly=True)

    # get all messages since since_date and before before_date
    typ, [msg_ids] = mail.search(None,
        '(since "%s" before "%s")' % (since_date.strftime(date_format),
                                      before_date.strftime(date_format)))

    # get complete email messages in RFC822 format
    for num in msg_ids.split():
        typ, msg_data = mail.fetch(num, '(RFC822)')
        for response_part in msg_data:
            if isinstance(response_part, tuple):
                msg = email.message_from_bytes(response_part[1])
                for header in [ 'subject', 'to', 'from', 'date' ]:
                    print('%-8s: %s' % (
                        header.upper(), decode_header(msg[header])))
                print(get_text(msg))
finally:
    try:
        mail.close()
    finally:
        mail.logout()

See imap-fetch-email-before-date.py.

Mcdonough answered 3/3, 2014 at 22:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.