【问题标题】:python: win32com bulk save attachment error - server admin has limited number of itemspython:win32com批量保存附件错误-服务器管理员的项目数量有限
【发布时间】:2022-11-10 22:32:59
【问题描述】:

我正在遍历存储在数据框中(从 csv 文件加载)中的 entryId,并通过调度 win32com.client 访问 Outlook MAPI 并使用以下代码将电子邮件附件保存到本地目录来访问消息。我还将附件名称、路径和 entryId 存储在一个新的数据框中以供以后分析。

  • Outlook 版本:2202(内部版本 14931.20764)
  • Pywin32 版本:227
  • Python 版本:3.7.1
df = pd.DataFrame(columns=['attName', 'path', 'entryId'])
id = 1
for email in emailData.itertuples():
    outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
    message = outlook.GetItemFromID(email.entryId)
    if message:
        receivedDate = message.ReceivedTime
        if message.Attachments.Count > 0:
           for attachment in message.Attachments:
                if attachment.Type in {1,4,5}:
                    if not attachment.DisplayName.endswith('.png') and not attachment.DisplayName.endswith('.jpg') and not attachment.DisplayName.endswith('.gif'):
                        attName = str(attachment.DisplayName)
                        print('\t Attachment: %s' % attachment.DisplayName)
                        path = "some directory\\%s\\%s" % (receivedDate.year, attachment.DisplayName)
                        attachment.SaveAsFile(path) #if I remove this line, the error no longer occurs
                        attachment = None
                        df.loc[id] = ([attName, str(path), email.entryId])
                        id += 1
            attachments = None
        message.Close(1)
        outlook.Logoff()
        outlook = None

扫描 248 条消息后,无论特定消息如何,都会遇到以下错误:

  File "C:\Anaconda3\envs\myenv\lib\site-packages\win32com\client\__init__.py", line 474, in __getattr__
    return self._ApplyTypes_(*args)
  File "C:\Anaconda3\envs\myenv\lib\site-packages\win32com\client\__init__.py", line 467, in _ApplyTypes_
    self._oleobj_.InvokeTypes(dispid, 0, wFlags, retType, argTypes, *args),
pywintypes.com_error: (-2147352567, 'Exception occurred.', (4096, 'Microsoft Outlook', 'Your server administrator has limited the number of items you can open simultaneously. Try closing messages you have opened or removing attachments and images from unsent messages you are composing.', None, 0, -2147220731), None)

我能够将错误专门隔离到这一行:

attachment.SaveAsFile(path)

如果我删除此行,错误就会消失,并将继续扫描消息。我不确定是什么导致了这个错误,我尝试了各种命令来关闭/删除对附件的引用,方法是将对象设置为 None 并使用 outlook.Logoff() 作为命名空间。

有没有其他人遇到过这个问题或有什么办法解决它?

更新:在阅读了Eugene Astafiev 的有用建议后,我对我的代码进行了一些更新,以帮助表明问题出在 attachment.SaveAsFile(path) 行上。不幸的是,我仍然收到完全相同的错误。也许我不明白如何释放对象?任何人都可以提供进一步的帮助吗?

outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
for email in emailData.itertuples():
    message = outlook.GetItemFromID(email.entryId)
    if message:
       attachments = []
       for attachment in list(message.Attachments):
           attachments.append(attachment)
       for attachment in attachments:
            attachType = int(attachment.Type)
            if attachType in {1,4,5}: 
                attName = str(attachment.DisplayName)
                if not attName.endswith('.png') and not attName.endswith('.jpg') and not attName.endswith('.gif'):
                    path = "somedir\\%s" % (attName)
                    attachment.SaveAsFile(path) #Error disappears if this line is removed
                    del attachment
                    del path
                del attName
            del attachType
        del attachments
    message.Close(1)
    del message

【问题讨论】:

    标签: python outlook win32com mapi office-automation


    【解决方案1】:

    这是在 Outlook 中处理 Exchange 配置文件时普遍存在的问题。您必须尽快释放所有 Outlook COM 引用以防止达到限制。

    首先,我建议创建一次 Outlook 实例,然后为循环中处理的每条消息重新使用它。

    outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
    
    for email in emailData.itertuples():
        message = outlook.GetItemFromID(email.entryId)
        if message:
    

    其次,您需要减少所有 Outlook COM 对象的引用计数器,因此您将尽量避免该限制。请参阅how to release used memory immediately in python list? 了解更多信息。

    第三,打断属性和方法调用链,这样集合也可以被释放,例如:

     if message.Attachments.Count > 0:
    

    Attachments 属性返回应在之后释放的 Attachments 类的实例。但是如果在这样的代码中使用它,它将永远不会被释放,你将很容易达到极限。

    【讨论】:

    • 感谢您的快速回复。我已根据您的建议修改了代码,但仍然出现错误
    • 我仍然看到您没有在代码中释放 Outlook COM 对象。请注意,每次调用属性时,COM 引用计数器都会增加,因此您需要在之后释放引用计数器。
    • 我不明白,在我将附件.SaveAsFile(path) 添加到我的代码之前,我扫描 10,000 多封电子邮件没有问题......你能给我一个在 python 中释放 COM 对象的例子吗?我阅读了您链接的帖子,这就是我使用 DEL 关键字的原因。真的我需要消除对 COM 对象的所有引用。但是在这个post 状态下,除非我尝试实现weakref,否则这是不可能的。我在这里做错了什么?
    【解决方案2】:

    在对如何在 python 中释放 COM 对象进行了大量研究之后,我从未找到解决方案。对我来说,SaveAsFile() 方法有一个错误,它会永久引用每条消息,因此一旦我扫描了 248 条消息并达到管理员限制,这个错误就无法解决。

    相反,我制定了一个解决方案来计算已扫描邮件的数量,直到扫描 245 封邮件并强制 Outlook 关闭并再次打开以恢复扫描。我能够在一个脚本会话中扫描约 12,000 条带有附件的邮件。我添加了一些时间缓冲区,以确保每次 Outlook 重新启动之间的平稳过渡。

    if i == 245:
        outlook.Application.Quit()
        del outlook
        time.sleep(20)
        os.system('taskkill /im outlook.exe /f') #bit redundant but ensures task is closed
        time.sleep(30)
        openR = os.system('start outlook')
        time.sleep(120)
        outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
        i = 0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-08
      • 1970-01-01
      • 2021-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-05
      相关资源
      最近更新 更多