56

I'm trying to write a short program that will read in the contents of e-mails within a folder on my exchange/Outlook profile so I can manipulate the data. However I'm having a problem finding much information about python and exchange/Outlook integration. A lot of stuff is either very old/has no docs/not explained. I've tried several snippets but seem to be getting the same errors. I've tried Tim Golden's code:

import win32com.client

session = win32com.client.gencache.EnsureDispatch ("MAPI.Session")

#
# Leave blank to be prompted for a session, or use
# your own profile name if not "Outlook". It is also
# possible to pull the default profile from the registry.
#
session.Logon ("Outlook")
messages = session.Inbox.Messages

#
# Although the inbox_messages collection can be accessed
# via getitem-style calls (inbox_messages[1] etc.) this
# is the recommended approach from Microsoft since the
# Inbox can mutate while you're iterating.
#
message = messages.GetFirst ()
while message:
    print message.Subject
    message = messages.GetNext ()

However I get an error:

pywintypes.com_error: (-2147221005, 'Invalid class string', None, None)

Not sure what my profile name is so I tried with:

session.Logon()

to be prompted but that didn't work either (same error). Also tried both with Outlook open and closed and neither changed anything.

Calon
  • 4,174
  • 1
  • 19
  • 30
johnharris85
  • 17,264
  • 5
  • 48
  • 52
  • 1
    Have you considered using IMAP against the server rather than depending on an Outlook client? Depending on your use-case, IMAP may prove viable and much more portable (both clients and servers). – Jason R. Coombs Feb 22 '11 at 12:04
  • 1
    @Jason IMAP looks good but unfortunately is not enabled on the account I'm using. – johnharris85 Feb 22 '11 at 16:36

4 Answers4

84

I had the same problem you did - didn't find much that worked. The following code, however, works like a charm.

import win32com.client

outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")

inbox = outlook.GetDefaultFolder(6) # "6" refers to the index of a folder - in this case,
                                    # the inbox. You can change that number to reference
                                    # any other folder
messages = inbox.Items
message = messages.GetLast()
body_content = message.body
print body_content
Bobby
  • 1,062
  • 9
  • 9
  • 3
    Is there a way to see the other message attributes? I would like to use your example to get the date and time the message was received. – sequoia Oct 02 '13 at 00:47
  • 7
    found the solution: `message.CreationTime` for some reason the attributes don't come up when I use dir() – sequoia Oct 02 '13 at 01:14
  • 11
    @sequoia - Using Microsoft's list of COM properties for MailItems (e.g.: Outlook messages) is how I found all the properties of message (message.SenderEmailAddress, for example): http://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook.mailitem_properties.aspx – Ward W Feb 25 '14 at 21:48
  • @sequoia - Usually I use IPython when I want to figure out what methods an object might contain. In this case, for example, typing "messages." then pressing TAB will give you a list of methods you can call. Glad you figured out what you needed though. – Bobby Sep 13 '14 at 14:54
  • Apologies @Bobby, completely forgot about this question (and to accept your answer!) – johnharris85 Jul 15 '15 at 17:51
  • I don't like this answer because it limits your folders. How about aggregating all folders into a data structure ? See below .......... ALSO --- to see message attributes, ...https://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook.mailitem_members.aspx .. use interactive console, use `dir()` and `_prop_map_get_` on the object... keep it simple. – Zombro May 13 '17 at 17:14
  • 2
    GetDefaultFolder-Params can be found at https://msdn.microsoft.com/de-de/library/office/ff861868.aspx – Kurt Ludikovsky Aug 05 '17 at 08:57
  • 1
    I have three outlook profiles setup (three email accounts). How do I choose which profile to use in python? – user5155835 Nov 16 '17 at 08:20
  • 4
    "6" refers to the inbox folder. How about the other folders? If you create a new folder, how do you know which sequential number it gets? – FaCoffee Aug 07 '18 at 09:16
  • 1
    @FaCoffee To get a custom folder: `inbox = outlook.GetDefaultFolder(6).Parent.Folders("Test")` Replace Test with the name of your folder This is assuming the folder is at the same level as the Inbox folder – Nicolasome Jan 26 '21 at 05:06
  • 1
    @FaCoffee You can refer to this [document](https://learn.microsoft.com/en-us/dotnet/api/microsoft.office.interop.outlook.oldefaultfolders?view=outlook-pia) – Amit Pathak Mar 25 '21 at 17:41
  • @Bobby amazing code, it is the most easy and straight forward I got on internet. But what, when we need to read specific email. Like I want to filter it with subject line – Jayank Mar 26 '21 at 10:00
  • Hi does this MAPI work without connecting to outlook locally or it requires internet connection? – Kenneth Singh Sep 28 '22 at 16:32
13

I have created my own iterator to iterate over Outlook objects via python. The issue is that python tries to iterates starting with Index[0], but outlook expects for first item Index[1]... To make it more Ruby simple, there is below a helper class Oli with following methods:

.items() - yields a tuple(index, Item)...

.prop() - helping to introspect outlook object exposing available properties (methods and attributes)

from win32com.client import constants
from win32com.client.gencache import EnsureDispatch as Dispatch

outlook = Dispatch("Outlook.Application")
mapi = outlook.GetNamespace("MAPI")

class Oli():
    def __init__(self, outlook_object):
        self._obj = outlook_object

    def items(self):
        array_size = self._obj.Count
        for item_index in xrange(1,array_size+1):
            yield (item_index, self._obj[item_index])

    def prop(self):
        return sorted( self._obj._prop_map_get_.keys() )

for inx, folder in Oli(mapi.Folders).items():
    # iterate all Outlook folders (top level)
    print "-"*70
    print folder.Name

    for inx,subfolder in Oli(folder.Folders).items():
        print "(%i)" % inx, subfolder.Name,"=> ", subfolder
Martin Stancik
  • 339
  • 2
  • 4
4

Sorry for my bad English. Checking Mails using Python with MAPI is easier,

outlook =win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
folder = outlook.Folders[5]
Subfldr = folder.Folders[5]
messages_REACH = Subfldr.Items
message = messages_REACH.GetFirst()

Here we can get the most first mail into the Mail box, or into any sub folder. Actually, we need to check the Mailbox number & orientation. With the help of this analysis we can check each mailbox & its sub mailbox folders.

Similarly please find the below code, where we can see, the last/ earlier mails. How we need to check.

`outlook =win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
folder = outlook.Folders[5]
Subfldr = folder.Folders[5]
messages_REACH = Subfldr.Items
message = messages_REACH.GetLast()`

With this we can get most recent email into the mailbox. According to the above mentioned code, we can check our all mail boxes, & its sub folders.

Ozzius
  • 131
  • 2
  • 8
2

I had the same issue. Combining various approaches from the internet (and above) come up with the following approach (checkEmails.py)

class CheckMailer:

        def __init__(self, filename="LOG1.txt", mailbox="Mailbox - Another User Mailbox", folderindex=3):
            self.f = FileWriter(filename)
            self.outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI").Folders(mailbox)
            self.inbox = self.outlook.Folders(folderindex)


        def check(self):                
        #===============================================================================
        # for i in xrange(1,100):                           #Uncomment this section if index 3 does not work for you
        #     try:
        #         self.inbox = self.outlook.Folders(i)     # "6" refers to the index of inbox for Default User Mailbox
        #         print "%i %s" % (i,self.inbox)            # "3" refers to the index of inbox for Another user's mailbox
        #     except:
        #         print "%i does not work"%i
        #===============================================================================

                self.f.pl(time.strftime("%H:%M:%S"))
                tot = 0                
                messages = self.inbox.Items
                message = messages.GetFirst()
                while message:
                    self.f.pl (message.Subject)
                    message = messages.GetNext()
                    tot += 1
                self.f.pl("Total Messages found: %i" % tot)
                self.f.pl("-" * 80)
                self.f.flush()

if __name__ == "__main__":
    mail = CheckMailer()
    for i in xrange(320):  # this is 10.6 hours approximately
            mail.check()
            time.sleep(120.00)

For concistency I include also the code for the FileWriter class (found in FileWrapper.py). I needed this because trying to pipe UTF8 to a file in windows did not work.

class FileWriter(object):
    '''
    convenient file wrapper for writing to files
    '''


    def __init__(self, filename):
        '''
        Constructor
        '''
        self.file = open(filename, "w")

    def pl(self, a_string):
        str_uni = a_string.encode('utf-8')
        self.file.write(str_uni)
        self.file.write("\n")

    def flush(self):
        self.file.flush()
serenesat
  • 4,611
  • 10
  • 37
  • 53
raptisth
  • 62
  • 3