
How to read email from outlook in python
There are always scenarios that you may wonder how to have a program to automatically read email from outlook and do some processing based on certain criteria. The most common use case is that you want to auto process email attachments when receiving some scheduled reports. In this article, I will be explaining to you how to use python to read email from outlook and save attachment files into the specified folder.
Prerequisites:
In order to be able to access the outlook native application, we will need to make use of the pywin32 library. Make sure you have installed this library and imported into your script.
import win32com.client #other libraries to be used in this script import os from datetime import datetime, timedelta
Let’s get started!
Like communicating with other system or app, you will need to initiate a session in the first place. By calling the GetNamespace function, you can get the outlook session for the subsequent operations.
outlook = win32com.client.Dispatch('outlook.application') mapi = outlook.GetNamespace("MAPI")
if you have configured multiple accounts in your outlook, you need to pass in the account name when accessing it’s folders, we can cover this topic in another article. For this article, let assume we only have 1 account configured in outlook.
for account in mapi.Accounts: print(account.DeliveryStore.DisplayName)
To access the inbox folder, you will need to pass in the folder type – 6 in the below function. You may refer to this doc to understand the full list of folder types, such as the draft, outbox, sent, deleted items folder etc.
inbox = mapi.GetDefaultFolder(6)
What if your email is in a sub folder under your inbox? The GetDefaultFolder has the Folders attribute where you can access to the sub folder by it’s name. For instance, to access the “your_sub_folder” under the inbox folder:
inbox = mapi.GetDefaultFolder(6).Folders["your_sub_folder"]
Read email from outlook
Now you are accessible to the inbox and it’s sub folder. You can view all the messages by getting the items as per below. But you may want filter the messages by certain criteria, such as the receiving date, from, subject etc. To do that, we can apply some filter conditions to the messages.
messages = inbox.Items
Use Restrict function to filter your email message. For instance, we can filter by receiving time in past 24 hours, and email sender as “contact@codeforests.com” with subject as “Sample Report”
received_dt = datetime.now() - timedelta(days=1) received_dt = received_dt.strftime('%m/%d/%Y %H:%M %p') messages = messages.Restrict("[ReceivedTime] >= '" + received_dt + "'") messages = messages.Restrict("[SenderEmailAddress] = 'contact@codeforests.com'") messages = messages.Restrict("[Subject] = 'Sample Report'")
Save attachment files
With all the above filters, we shall only have the messages that we are interested in. Let’s loop through the message and check for the details.
#Let's assume we want to save the email attachment to the below directory outputDir = r"C:\attachment" try: for message in list(messages): try: s = message.sender for attachment in message.Attachments: attachment.SaveASFile(os.path.join(outputDir, attachment.FileName)) print(f"attachment {file_name} from {s} saved") except Exception as e: print("error when saving the attachment:" + str(e)) except Exception as e: print("error when processing emails messages:" + str(e))
There are other attributes like Body, Size, Subject, LastModificationTime etc., please check this Microsoft documentation for more details.
You may be also interested in how to send email from outlook in python, please check this article.
As per always, welcome any comments or questions.
Great post
Thank you.
I am getting an error while I try using Restrict, could you help?
pywintypes.com_error: (-2147352567, ‘Exception occurred.’, (4096, ‘Microsoft Outlook’, ‘Cannot parse condition. Error at “\xa0”.’, None, 0, -2147352567), None)
Hi, thanks for reading. It seems like you have copied some special character (non-breaking space) in your code when forming the conditions, Do you mind to share some sample code here for checking?
Thank you for the fast reply.
I managed to resolve this when using Restrict without any spaces before and after the equal sign.
Do you know how can use regex to filter emails? Let us say only messages from .*@gmail.com?
hi, you can try the below and see if it works for you:
messages = messages.Restrict(“@SQL=(urn:schemas:httpmail:sender LIKE ‘%gmail.com%’)”)
The senderemail seems not working in this case.
The other way is to directly get the send address and then do a string comparison:
if message.SenderEmailType == “EX”:
sender_address = message.Sender.GetExchangeUser().PrimarySmtpAddress
else:
# email type SMTP
sender_address = message.SenderEmailAddress
if sender_address.endswith(“gmail.com”):
…
Thank you, I will try.
I wanted to avoid checking and only run a specific list.
Thank you very much for your help!
How to get latest n mails?
You can use the Sort function to sort the messages by received time in descending order.
Something like below:
filtered_messages.Sort(“[ReceivedTime]”, Descending=True)
for message in list(filtered_messages)[:n]:
print(message.Subject, message.ReceivedTime)
Thank you very much Ken. It works perfectly. You really saved my day.
You’re welcome, Anurag -:)
getting invalid character in identifier error in line
messages = messages.Restrict(“[ReceivedTime] >= ‘” + received_dt + “‘”)
^
how do i resolve?
Hi Henry,
I noticed the double quotes were converted to unicode which causes the problem. Can you try the below?
messages = messages.Restrict(“[ReceivedTime] >= ‘” + received_dt + “‘”)
Hi, I have linked my gmail account to Outlook but now I think there is the case where you said that I have to pass in the account name when accessing it’s folders. In Outlook i have the section for Outlook and below a section for my gmail-address. This is the one that I’m trying to access. Where do I have to do that?
Hi, do you want to try the below and see whether it works for you to access the different mailbox?
inbox1 = mapi.Folders(‘abc@gmail.com’).Folders(‘Inbox’)
inbox2 = mapi.Folders(‘def@xxx.com’).Folders(‘Inbox’)
Now I’m getting this error:
Traceback (most recent call last):
File “M:/Raphael/Programming/Python/TestProject/main.py”, line 12, in <module>
inbox1 = folder.Folders(‘Inbox’)
File “M:\Programme\anaconda3\envs\TestProject\lib\site-packages\win32com\client\dynamic.py”, line 197, in __call__
return self._get_good_object_(self._oleobj_.Invoke(*allArgs),self._olerepr_.defaultDispatchName,None)
pywintypes.com_error: (-2147352567, ‘Exception occurred.’, (4096, ‘Microsoft Outlook’, ‘Der versuchte Vorgang konnte nicht ausgeführt werden. Ein Objekt wurde nicht gefunden.’, None, 0, -2147221233), None)
Note:
I split the line inbox1 = mapi.Folders(‘abc@gmail.com).Folders(‘Inbox’) into two lines to see where the error is coming from
Hi, I was able to get the email messages from the inbox folder with below (although I do not have two accounts configured in outlook). Are you able to access any of your account?
messages = mapi.Folders(‘abc@gmail.com’).Folders(‘Inbox’).Items
for m in messages:
print(m)
Hello- what if I have two accounts configured in outlook?
Hi, do you want to try if the below works? If there are multiple accounts configured, you will need to specify the account id to be accessed. Each account is basically a separate folder.
messages = mapi.Folders(‘abc@gmail.com’).Folders(‘Inbox’).Items
for m in messages:
print(m)
Hi, This was a great help for me, although my requirement is not getting fulfilled by this, I need to download the attachment from outlook using just a sender and a particular date. Could you take a look at it?
import win32com.client
import os
from datetime import datetime, timedelta
Outlook = win32com.client.Dispatch(“Outlook.Application”)
mapi=Outlook.GetNamespace(“MAPI”)
Inbox = mapi.GetDefaultFolder(6)
Messages = Inbox.Items
ReceivedDate = datetime.now()
ReceivedDate = ReceivedDate.strftime(‘%m/%d/%Y’)
messages = Messages.Restrict(“[ReceivedTime] = ‘”+ReceivedDate+”‘”)
messages = Messages.Restrict(“[SenderEmailAddress]=’example@outlook.com'”)
OutputDir = r”C:\Users\usename\Desktop”
try:
for message in list(messages):
try:
s = message.sender
print(s)
for attachment in message.Attachments:
print(attachment)
attachment.SaveASFile(os.path.join(OutputDir, attachment.FileName))
print(f”attachment {actualfilenamewithextension} from {s} saved”)
except Exception as e:
print(“error when saving the attachment:” + str(e))
except Exception as e:
print(“error when processing emails messages:” + str(e))
Hi Sai,
Received date format has to be ‘%m/%d/%Y %H:%M %p’ format, and it does not accept a date without hour & minutes. So if you just want to filter the emails on a particular date, you probably have to use restrict twice, e.g.:
from datetime import datetime, timedelta
received_dt_start = datetime.datetime(2021,2,22,0,0,0)
received_dt_end = received_dt_start + timedelta(hours=24)
messages = messages.Restrict(“[ReceivedTime] >= ‘” + received_dt_start + “‘”)
messages = messages.Restrict(“[ReceivedTime] <= '" + received_dt_end + "'")
Hi Ken, thanks for your quick look into this, not sure what’s wrong I cannot still make this work.
So, I want to take the start and end dates as variables so I used this.
import win32com.client
import os
from datetime import datetime, timedelta, date
Outlook = win32com.client.Dispatch(“Outlook.Application”)
mapi=Outlook.GetNamespace(“MAPI”)
Inbox = mapi.GetDefaultFolder(6)
Messages = Inbox.Items
dt = date.today()
received_dt_start=(datetime.combine(dt, datetime.min.time()))
received_dt_end = received_dt_start + timedelta(hours=24)
print(received_dt_start)
print(received_dt_end)
messagesfilter = Messages.Restrict(“[ReceivedTime] >= ‘” + received_dt_start + “‘”)
messagesfilter = Messages.Restrict(“[ReceivedTime] <= ‘” + received_dt_end + “‘”)
messagesfilter = Messages.Restrict(“[SenderEmailAddress]=’example@outlook.com'”)
OutputDir = r”C:\Users\username\Desktop”
try:
for message in list(messagesfilter):
try:
s = message.sender
print(s)
for attachment in message.Attachments:
print(attachment)
attachment.SaveASFile(os.path.join(OutputDir, attachment.FileName))
print(f”attachment {actualfilenamewithextension} from {s} saved”)
except Exception as e:
print(“error when saving the attachment:” + str(e))
except Exception as e:
print(“error when processing emails messages:” + str(e))
Hi Sai,
Based on what you’ve posted, you will have to format your datetime object to a string when passing to the Restrict function, e.g:
Messages.Restrict(“[ReceivedTime] >= ‘” + received_dt_start.strftime(‘%m/%d/%Y %H:%M %p’) + “‘”)
Hi Ken, I’ve changed the date format to m/d/y hh: mm am but the script is not going through the try and except method loop, if it goes through satisfying the condition it should give us the sender’s name if it’s not satisfied it should at least give the final except condition “error when processing emails” but that’s not happening. I’ve looked at the loop and as per my understanding it’s correct could you please check this?
messagesfilter = Messages.Restrict(“[ReceivedTime] >= ‘” + received_dt_start.strftime(‘%m/%d/%Y %H:%M %p’) + “‘”)
messagesfilter = Messages.Restrict(“[ReceivedTime] <= ‘” + received_dt_end.strftime(‘%m/%d/%Y %H:%M %p’) + “‘”)
messagesfilter = Messages.Restrict(“[SenderEmailAddress]=’example@outlook.com'”)
OutputDir = r”C:\Users\username\Desktop”
try:
for message in list(messagesfilter):
try:
subject=message.Subject
print(subject)
sender = message.Sender
print(sender)
for attachment in message.Attachments:
print(attachment)
attachment.SaveASFile(os.path.join(OutputDir, attachment.FileName))
print(f”attachment {actualfilenamewithextension} from {s} saved”)
except Exception as e:
print(“error when saving the attachment:” + str(e))
except Exception as e:
print(“error when processing emails messages:” + str(e))
hi Sai,
Your code itself seems ok. I suggest you do step by step debugging to find out where is the issue. First try to print out email messages without using the Restrict method, then later add in the received time filter (maybe with a wider date range).