3

I need to delete multiple email messages in Outlook from python via win32com module.

I understand there is a VBA method MailItem.Delete() available to win32com via COM and it works; but it is VERY VERY slow when deleting more than one email since one would have to delete emails sequentially ie loop over the MailItem collection of emails.

Is there any way to delete a selected collection of mailItems at once, something like MailItemCollection.DeleteAll()?

Also, if above is not possible; is it at all possible to delete many emails via multi-threaded approach ie divide the collection of mailItems into, let's say, 4 subsets; have 4 threads operate on those?

I figure since I can delete multiple emails in outlook via its GUI very fast, there has to be a way where I can do the same thing via COM API.

8
  • @TheIncorrigible1, I would if I could. I must use python... and you are going off topic...
    – JavaFan
    Commented Nov 28, 2018 at 16:45
  • @TheIncorrigible1 - the issue here is the performance of underlying API when used against on online or a large folder, not the overhead of using Python vs any other language Commented Nov 28, 2018 at 16:46
  • @DmitryStreblechenko When using the outlook com object, you're essentially using whatever office version is installed and emulating click actions so it's only as slow as whatever application is being used (e.g., 2013 v 2016) and the hardware of your host. You're not directly interfacing with an outlook server; the outlook application is. Commented Nov 28, 2018 at 16:50
  • @TheIncorrigible1 - absolutely not: Python can access Outlook Object Model in the same fashion as any other language such as VBS or Powershell, no clicks are emulated. You are probably thinking of late binding vs early binding, which is negligible in this case. Commented Nov 28, 2018 at 17:01
  • @DmitryStreblechenko I didn't mean literal click emulation, but the office com objects are doing essentially that. I can't think of any functions available to the APIs that you couldn't access through the GUI off the top of my head. Commented Nov 28, 2018 at 17:02

6 Answers 6

3

Not in OOM - MailItem.Delete or Items.Remove(Index) is all you get.

On the Extended MAPI level (C++ or Delphi, but not Python), you can delete multiple messages using IMAPIFolder.DeleteMessages (which takes a list of entry ids). Or you can use IMAPIFolder.EmptyFolder (deletes all messages in a folder).

If using Redemption (any language; I am its author) is an option, you can use RDOFolder2.EmptyFolder or RDOFolder.Items.RemoveMultiple. RDOFolder can be retrieved from RDOSession.GetRDOObjectFromOutlookObject if you pass Outlook's MAPIFolder object as a parameter.

1
  • Thanks for a confirmation - makes sense.
    – JavaFan
    Commented Nov 29, 2018 at 16:44
3

On top of a great answer by @Dimitry I'll add a remark which may be important for you: if you start deleting from Items as you iterate over it, strange things may happen. For example on my system the following Python code:

for mail in folder.Items:
    mail.Delete()

as well as

for index, mail in enumerate(folder.Items, 1):
    folder.Remove(index)

both remove only half of the items in the folder! The reason seems to be that Items uses a range of indices internally to provide an iterator so each time an element is deleted, the tail of the list is shifted by one...

To remove all items in the folder try:

for i in range(len(folder.Items)):
    folder.Remove(1)

If you need to filter by a certain criterion consider first gathering EntryIDs and then deleting searching for ID:

ids = []
for i in range(len(folder.Items), 1):
    if to_be_deleted(folder.Items[index]):
        ids.append(index)
for id in ids:
    outlook.GetEntryByID(id).Delete()

I imagine performance of that is even worse, though :c

2
  • I was thinking... since python can run c-programs via cpython. is it possible then to invoke a solution in cpython that deletes emails much faster than regular win32com interface?
    – JavaFan
    Commented Feb 6, 2019 at 15:58
  • I can see no reason why this would fail. The only problem may be that this c code needs to connect to Outlook on its own, afaik. You should be able to pass relevant IDs for easy retrieval of Store/Account/Folder of your interest...
    – Dedalus
    Commented Feb 6, 2019 at 21:54
3

Great answer from Dedalus above. Wanted to make a more concise version of the code:

import win32com.client

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

# Select main Inbox
inbox = outlook.GetDefaultFolder(6)
messages = inbox.Items

# Delete all messages from a specific sender
sender = '[email protected]'
try:
    for message in messages:
        try:
            s = message.sender
            s = str(s)
            if s == sender:
                 message.Delete()
        except:
            pass
except:
    pass

You may not need two "trys" but I found it was more stable when applying the script to a long and heavily used inbox. Usually I combine this with a script that limits the message = inbox.Items to within a week so it doesn't do the entire inbox.

2

For me it worked by iterating the items in reverse.

Old:

for mail in folder.Items:
    if 'whatever' in mail.Subject: # just a condition (optional)
        mail.Delete()

New code:

for mail in reversed(folder.Items): # just tried deleting Items in reverse order
    if 'whatever' in mail.Subject: # just a condition (optional)
        mail.Delete()

Hope this helps someone.

2
  • @GeorgeJoseph I use Microsoft's website to get an overview of the object interface. I couldn't find a perm delete method. What I do is, go in the "Deleted" folder and call .Delete() on the mail items in there too. Commented Feb 11, 2020 at 17:57
  • @GeorgeJoseph To be more specific, I read now in the Object Model documentation - MailItem.Delete() why you need to do reverse iteration i.e. start from the last item in the collection of items when deleting, and it specifies also that the item is permanently deleted if .Delete() is called on items in the "Deleted" folder. So it actually seems to be the way Microsoft suggests too! Commented Feb 11, 2020 at 18:44
1

Am I missing something? Neither Application nor NameSpace objects appear to have a GetEntryByID method, though the rest of what Dedalus pointed out was correct.

Namespace objects have a GetItemFromID method, and MailItem objects have a EntryID property which will uniquely identify them so long as they don't get reorganized into different folders.

Documentation: https://learn.microsoft.com/en-us/office/vba/outlook/how-to/items-folders-and-stores/working-with-entryids-and-storeids

My full solve:

import win32com.client

outlook = win32com.client.gencache.EnsureDispatch("Outlook.Application")

folders = outlook.GetNamespace("MAPI")

inbox= folders.GetDefaultFolder(6)

messages=inbox.Items

email_ids = []

folder_id = inbox.StoreID 

# Here create a function to isolate/exclude. Below is just an example of filtering by a subject line.

email_subjects = ['Subj1','Subj2','Subj3']

for i in range(len(messages)):

    if any(header in inbox.Items[i].Subject for header in email_subjects):

        email_ids.append(inbox.Items[i].EntryID)

for id in email_ids:
    folders.GetItemFromID(id, folder_id).Delete()
1
  • how fast is your solution? I am using older approach ie delete last message in a list and it takes forever. (server and client are on different continents). Do you think your proposed approach is a bit faster?
    – JavaFan
    Commented Jan 15, 2020 at 21:33
0

I've implemented an alternative solution in local Outlook, by moving email ítems from.inbox folder to deleted items folder or to an archive folder, by using VBA code or Outlook filter rules directly. This way, I just mannualy empty the deleted items folder once a week (of course this periodic step can also be programmed). I observed that this strategy can be more efficient instead of delete item per item using code (you mentioned the internal.indexes problem).

1
  • 1
    As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Jan 31, 2022 at 0:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.