Getting n most recent emails using IMAP and Python

Question:

I’m looking to return the n (most likely 10) most recent emails from an email accounts inbox using IMAP.

So far I’ve cobbled together:

import imaplib
from email.parser import HeaderParser

M = imaplib.IMAP4_SSL('my.server')
user = 'username'
password = 'password'
M.login(user, password)
M.search(None, 'ALL')
for i in range (1,10):
    data = M.fetch(i, '(BODY[HEADER])')
    header_data = data[1][0][1]
    parser = HeaderParser()
    msg = parser.parsestr(header_data)
    print msg['subject']

This is returning email headers fine, but it seems to be a semi-random collection of emails that it gets, not the 10 most recent.

If it helps, I’m connecting to an Exchange 2010 server. Other approaches also welcome, IMAP just seemed the most appropriate given that I only wanted to read the emails not send any.

Asked By: mrmagooey

||

Answers:

The sort command is available, but it is not guaranteed to be supported by the IMAP server. For example, Gmail does not support the SORT command.

To try the sort command, you would replace:
M.search(None, 'ALL')
with
M.sort(search_critera, 'UTF-8', 'ALL')

Then search_criteria would be a string like:

search_criteria = 'DATE' #Ascending, most recent email last
search_criteria = 'REVERSE DATE' #Descending, most recent email first

search_criteria = '[REVERSE] sort-key' #format for sorting

According to RFC5256 these are valid sort-key‘s:
"ARRIVAL" / "CC" / "DATE" / "FROM" / "SIZE" / "SUBJECT" / "TO"

Notes:
1. charset is required, try US-ASCII or UTF-8 all others are not required to be supported by the IMAP server
2. search critera is also required. The ALL command is a valid one, but there are many. See more at http://www.networksorcery.com/enp/rfc/rfc3501.txt

The world of IMAP is wild and crazy. Good luck

Answered By: rfadams

this is work for me~

import imaplib
from email.parser import HeaderParser
M = imaplib.IMAP4_SSL('my.server')
user = 'username'
password = 'password'
M.login(user, password)
(retcode, messages) =M.search(None, 'ALL')
 news_mail = get_mostnew_email(messages)
for i in news_mail :
    data = M.fetch(i, '(BODY[HEADER])')
    header_data = data[1][0][1]
    parser = HeaderParser()
    msg = parser.parsestr(header_data)
    print msg['subject']

and this is get the newer email function :

def get_mostnew_email(messages):
    """
    Getting in most recent emails using IMAP and Python
    :param messages:
    :return:
    """
    ids = messages[0]  # data is a list.
    id_list = ids.split()  # ids is a space separated string
    #latest_ten_email_id = id_list  # get all
    latest_ten_email_id = id_list[-10:]  # get the latest 10
    keys = map(int, latest_ten_email_id)
    news_keys = sorted(keys, reverse=True)
    str_keys = [str(e) for e in news_keys]
    return  str_keys
Answered By: LuciferJack

This is the code to get the emailFrom, emailSubject, emailDate, emailContent etc..

import imaplib, email, os
user = "[email protected]"
password = "pass"
imap_url = "imap.gmail.com"
connection = imaplib.IMAP4_SSL(imap_url)
connection.login(user, password)
result, data = connection.uid('search', None, "ALL")
if result == 'OK':
    for num in data[0].split():
        result, data = connection.uid('fetch', num, '(RFC822)')
        if result == 'OK':
            email_message = email.message_from_bytes(data[0][1])
            print('From:' + email_message['From'])
            print('To:' + email_message['To'])
            print('Date:' + email_message['Date'])
            print('Subject:' + str(email_message['Subject']))
            print('Content:' + str(email_message.get_payload()[0]))
connection.close()
connection.logout()        
Answered By: tshubham7
# get recent one email
from imap_tools import MailBox
with MailBox('imap.mail.com').login('[email protected]', 'password', 'INBOX') as mailbox:
   for msg in mailbox.fetch(limit=1, reverse=True):
       print(msg.date_str, msg.subject)

https://github.com/ikvk/imap_tools

Answered By: Vladimir

Workaround for Gmail. Since the The IMAP.sort(‘DATE’,’UTF-8′,’ALL’) does not work for gmail ,we can insert the values and date into a list and sort the list in reverse order of date. Can check for the first n-mails using a counter. This method will take a few minutes longer if there are hundreds of mails.

    M.login(user,password)
    rv,data= M.search(None,'ALL')
    if rv=='OK':
        msg_list=[]
        for num in date[0].split():
            rv,data=M.fetch(num,'(RFC822)')
            if rv=='OK':
                msg_object={}
                msg_object_copy={}
                msg=email.message_from_bytes(data[0][1])
                msg_date=""
                for val in msg['Date'].split(' '):
                    if(len(val)==1):
                        val="0"+val
                    # to pad the single date with 0
                    msg_date=msg_date+val+" "
                msg_date=msg_date[:-1]
              # to remove the last space
                msg_object['date']= datetime.datetime.strptime(msg_date,"%a, %d %b %Y %H:%M:%S %z")
            # to convert string to date time object for sorting the list
                msg_object['msg']=msg
                msg_object_copy=msg_object.copy()
                msg_list.append(msg_object_copy)
        msg_list.sort(reverse=True,key=lambda r:r['date'])
# sorts by datetime so latest mails are parsed first
        count=0
        for msg_obj in msg_list:
            count=count+1
            if count==n:
                break
            msg=msg_obj['msg']
        # do things with the message
Answered By: Sachu

To get the latest mail:

This will return all the mail numbers contained inside the 2nd return value which is a list containing a bytes object:

imap.search(None, "ALL")[1][0]

This will split the bytes object of which the last element can be taken by accessing the negative index:

imap.search(None, "ALL")[1][0].split()[-1]

You may use the mail number to access the corresponding mail.

Answered By: Aarav Prasad
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.