【问题标题】:python win32com: Delete multiple emails in Outlookpython win32com:在 Outlook 中删除多封电子邮件
【发布时间】:2022-01-30 20:03:19
【问题描述】:

我需要通过 win32com 模块从 python 中删除 Outlook 中的多封电子邮件。

我知道有一个 VBA 方法 MailItem.Delete() 通过 COM 可用于 win32com 并且它有效;但是当删除多于一封电子邮件时它非常非常慢,因为一个人必须按顺序删除电子邮件,即循环遍历邮件的 MailItem 集合。

有没有办法一次删除选定的邮件项集合,例如 MailItemCollection.DeleteAll()?

另外,如果上述方法是不可能的;是否有可能通过多线程方法删除许多电子邮件,即将mailItems 的集合划分为4 个子集;有4个线程在上面运行吗?

我认为,由于我可以通过其 GUI 非常快速地删除 Outlook 中的多封电子邮件,因此必须有一种方法可以通过 COM API 执行相同的操作。

【问题讨论】:

  • @TheIncorrigible1,如果可以的话,我会的。我必须使用 python ......而你跑题了......
  • @TheIncorrigible1 - 这里的问题是用于在线或大型文件夹时底层 API 的性能,而不是使用 Python 与任何其他语言的开销
  • @DmitryStreblechenko 使用 Outlook com 对象时,您实际上是在使用已安装的任何 Office 版本并模拟单击操作,因此它与正在使用的任何应用程序一样慢(例如,2013 v 2016)和主机的硬件。您不是直接与 Outlook 服务器交互; Outlook 应用程序是。
  • @TheIncorrigible1 - 绝对不行:Python 可以像 VBS 或 Powershell 等任何其他语言一样访问 Outlook 对象模型,不会模拟任何点击。您可能正在考虑后期绑定与早期绑定,在这种情况下可以忽略不计。
  • @DmitryStreblechenko 我不是指文字点击仿真,但办公室 com 对象基本上就是这样做的。我想不出任何 API 可用的功能,而您无法通过 GUI 访问我的脑袋。

标签: python api outlook com win32com


【解决方案1】:

不在 OOM 中 - MailItem.DeleteItems.Remove(Index) 是你所得到的。

在扩展 MAPI 级别(C++ 或 Delphi,但不是 Python)上,您可以使用 IMAPIFolder.DeleteMessages 删除多条消息(它采用条目 ID 列表)。或者您可以使用IMAPIFolder.EmptyFolder(删除文件夹中的所有消息)。

如果使用Redemption(任何语言;我是它的作者)是一个选项,您可以使用RDOFolder2.EmptyFolderRDOFolder.Items.RemoveMultiple。如果您将 Outlook 的 MAPIFolder 对象作为参数传递,则可以从 RDOSession.GetRDOObjectFromOutlookObject 检索 RDOFolder

【讨论】:

  • 感谢您的确认 - 有道理。
【解决方案2】:

在@Dimitry 的一个很好的答案之上,我将添加一个对您可能很重要的评论:如果您在迭代项目时开始从项目中删除,可能会发生奇怪的事情。 例如在我的系统上使用以下 Python 代码:

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

还有

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

两者都只删除了folder! 中的一半项目!原因似乎是 Items 在内部使用一系列索引来提供迭代器,因此每次删除一个元素时,列表的尾部都会移动一个...

要删除文件夹中的所有项目,请尝试:

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

如果您需要按特定条件过滤,请考虑先收集 EntryID,然后删除搜索 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()

不过,我想它的性能会更糟:c

【讨论】:

  • 我在想……因为 python 可以通过 cpython 运行 c 程序。那么是否有可能在 cpython 中调用一个比常规 win32com 界面更快地删除电子邮件的解决方案?
  • 我看不出这会失败的原因。唯一的问题可能是这个 c 代码需要自己连接到 Outlook,afaik。您应该能够传递相关 ID,以便轻松检索您感兴趣的商店/帐户/文件夹...
【解决方案3】:

上面 Dedalus 的回答很好。想做一个更简洁的代码版本:

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 = 'myname@abc.com'
try:
    for message in messages:
        try:
            s = message.sender
            s = str(s)
            if s == sender:
                 message.Delete()
        except:
            pass
except:
    pass

您可能不需要两次“尝试”,但我发现将脚本应用于冗长且频繁使用的收件箱时会更加稳定。通常我将此与将 message = inbox.Items 限制在一周内的脚本结合使用,因此它不会处理整个收件箱。

【讨论】:

    【解决方案4】:

    我错过了什么吗? Application 和 NameSpace 对象似乎都没有 GetEntryByID 方法,尽管 Dedalus 指出的其余部分是正确的。

    命名空间对象有一个 GetItemFromID 方法,而 MailItem 对象有一个 EntryID 属性,只要它们不被重新组织到不同的文件夹中,它将唯一地标识它们。

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

    我的完整解决方案:

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

    【讨论】:

    • 您的解决方案有多快?我正在使用较旧的方法,即删除列表中的最后一条消息,这需要永远。 (服务器和客户端位于不同的大陆)。您认为您提出的方法更快一些吗?
    【解决方案5】:

    对我来说,它通过反向迭代项目来工作。

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

    新代码:

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

    希望这对某人有所帮助。

    【讨论】:

    • @GeorgeJoseph 我使用Microsoft's website 来获得对象接口的概览。我找不到烫发删除方法。我所做的是,进入“已删除”文件夹,并在其中的邮件项目上调用 .Delete()。
    • @GeorgeJoseph 更具体地说,我现在在Object Model documentation - MailItem.Delete() 中阅读了为什么需要进行反向迭代,即在删除时从项目集合中的最后一项开始,它还指定如果对“已删除”文件夹中的项目调用 .Delete(),则项目将被永久删除。所以它实际上似乎也是微软建议的方式!
    【解决方案6】:

    我在本地 Outlook 中实现了另一种解决方案,通过直接使用 VBA 代码或 Outlook 过滤规则将电子邮件项目从.inbox 文件夹移动到已删除项目文件夹或存档文件夹。 这样,我每周只需手动清空一次已删除的项目文件夹(当然这个周期性步骤也可以编程)。 我观察到这种策略比使用代码删除每个项目更有效(你提到了 internal.indexes 问题)。

    【讨论】:

    • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 1970-01-01
    • 2015-07-25
    • 2022-01-13
    • 1970-01-01
    • 2021-06-16
    • 2010-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多