'python win32com: Delete multiple emails in Outlook

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.



Solution 1:[1]

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

Solution 2:[2]

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.

Solution 3:[3]

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://docs.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()

Solution 4:[4]

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.

Solution 5:[5]

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).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dedalus
Solution 2 Arthur D. Howland
Solution 3 Jordan Carney
Solution 4 Lefkios C. Geladaris
Solution 5 Metodio Cruz Espinoza