Pywin32 is one of the most popular packages for automating your daily work for Microsoft outlook/excel etc. In my previous post, we discussed about how to use this package to read emails and save attachments from outlook. As there were quite many questions raised in the comments which were not covered in the original post, this article is intended to review through some of the advanced topic for reading emails from outlook via Python Pywin32 package.
If you have not yet read through the previous post, you may check it out from here.
Assuming you have already installed the latest Pywin32 package and imported below necessary packages in your script, and you shall not encounter any error after executing the GetNamespace method to establish the outlook connection:
import win32com.client #other libraries to be used in this script import os from datetime import datetime, timedelta outlook = win32com.client.Dispatch('outlook.application') mapi = outlook.GetNamespace('MAPI')
When using below code to iterate the Accounts property, you shall see whichever accounts you have configured in your outlook:
for account in mapi.Accounts: print(account.DeliveryStore.DisplayName) #Assuming below accounts have been configured: #firstname.lastname@example.org #email@example.com
Now let’s move on to the topics we are going to discuss in this article.
Reading Email from Multiple Outlook Accounts
If you have multiple accounts configured in your outlook application, to access one of the accounts, you can use the Folders method and specify the account name or index of the account, e.g.:
for idx, folder in enumerate(mapi.Folders): #index starts from 1 print(idx+1, folder) #Assuming below output: # 1 firstname.lastname@example.org # 2 email@example.com
And to access the sub folders under a particular email account, you can continue to use the folders method to specify the sub folder name or index of the folder. Before that, you may want to check what are the available sub folders and it’s index value as per below:
for idx, folder in enumerate(mapi.Folders("firstname.lastname@example.org").Folders): print(idx+1, folder) # or using index to access the folder for idx, folder in enumerate(mapi.Folders(1).Folders): print(idx+1, folder)
You shall see something similar to the below:
With the above folder index and name, you shall be able to access the email messages as per below:
messages = mapi.Folders("email@example.com").Folders("Inbox").Items # or messages = mapi.Folders(1).Folders(2).Items for msg in list(messages): print(msg.Subject)
Although the index would not get changed when you move up/down of your folders in outlook, obviously using folder name still is much better than index in terms of readability of the code.
Filter Email Based on Receiving Time Window
When reading emails from outlook inbox, you may want to zoom into the emails within a specific receiving time window rather than scanning through thousands of emails you have received in the inbox. To filter emails based on certain conditions, you can use restrict method together with the logical operators.
For instance, to filter the emails received from 1st day of the current month until today 12am:
today = datetime.today() # first day of the month start_time = today.replace(month=1, hour=0, minute=0, second=0).strftime('%Y-%m-%d %H:%M %p') #today 12am end_time = today.replace(hour=0, minute=0, second=0).strftime('%Y-%m-%d %H:%M %p') messages = messages.Restrict("[ReceivedTime] >= '" + start_time + "' And [ReceivedTime] <= '" + end_time + "'")
With logical operators like AND, OR and NOT, you are able to combine multiple criteria together. For instance, to check the email with certain subject but not from a particular sender email:
messages = messages.Restrict("[Subject] = 'Sample Report'" + " And Not ([SenderEmailAddress] = 'firstname.lastname@example.org')")
And you can also use the Restrict method as many times as you wish if it makes your code more readable than combining all conditions in one filter, e.g.:
messages = messages.Restrict("[Subject] = 'Sample Report'") messages = messages.Restrict("Not ([SenderEmailAddress] = 'email@example.com')")
Getting First N emails
When using Restrict method for filtering email messages, you would not be able to specify max number of emails you want to read. If you wish to get the first/last N emails based on the receiving time, you can use the Sort method to sort the messages based on certain email properties before you slice the list. Below is the sample code to get the latest 10 email messages based on the receiving time:
messages.Sort("[ReceivedTime]", Descending=True) #read only the first 10 messages for message in list(messages)[:10]: print(message.Subject, message.ReceivedTime, message.SenderEmailAddress)
Wildcard Matching for Filtering
With the Restrict method, you cannot do wildcard matching such as searching whether the email subject or body contains certain keywords. To be able to achieve that, you will need to use the DASL query.
For instance, with the below DASL query syntax, you can filter email subject which contains “Sample Report” keyword:
messages = messages.Restrict("@SQL=(urn:schemas:httpmail:subject LIKE '%Sample Report%')")
You may want to check here to see what are the fields supported in ADSL query and the correct namespace to be used.
Include/Exclude Multiple Email Domains
To filter the emails only from a particular domain, you can use the ADSL query similar to the previous example:
messages = messages.Restrict("@SQL=(urn:schemas:httpmail:SenderEmailAddress LIKE '%company.com')")
And to exclude the emails from a few domains, you can use multiple conditions with logical operators:
messages = messages.Restrict("@SQL=(Not(urn:schemas:httpmail:senderemail LIKE '%@abc%') \ And Not(urn:schemas:httpmail:senderemail LIKE '%@123%') \ And Not(urn:schemas:httpmail:senderemail LIKE '%@xyz%'))")
In this article, we have reviewed through some advanced usage of the Pywin32 package for filtering emails. You may not find many Python tutorials for this package from online directly, but you shall be able to see the equivalent VBA code from its official website for most of the code you have seen in this article. In the event that you cannot find a solution for you problem, you may check and see whether there is something implemented in VBA code that you can convert it into Python syntax.