Python操作Outlook提取邮件附件时每次仅处理一半邮件的问题排查
问题背景
我在Unix上部署了定时任务,该任务每晚都会发送数百封邮件,我每天早上需要手动保存这些邮件的附件。为此我写了Python代码自动化处理,但每次运行脚本后,目标文件夹里只有一半左右的邮件附件被成功保存,剩余邮件未被处理。
运行日志
2021-11-18 06:13:30,688 : INFO : utils.util : Before clean up. 335 items in total 2021-11-18 06:13:42,098 : INFO : utils.util : After clean up. 167 items remained 2021-11-18 06:14:17,968 : INFO : utils.util : Before clean up. 167 items in total 2021-11-18 06:14:25,660 : INFO : utils.util : After clean up. 83 items remained 2021-11-18 06:14:34,762 : INFO : utils.util : Before clean up. 83 items in total 2021-11-18 06:14:38,591 : INFO : utils.util : After clean up. 41 items remained 2021-11-18 06:14:47,633 : INFO : utils.util : Before clean up. 41 items in total 2021-11-18 06:14:49,745 : INFO : utils.util : After clean up. 20 items remained 2021-11-18 06:14:56,348 : INFO : utils.util : Before clean up. 20 items in total 2021-11-18 06:14:57,426 : INFO : utils.util : After clean up. 9 items remained 2021-11-18 06:15:15,807 : INFO : utils.util : Before clean up. 9 items in total 2021-11-18 06:15:16,260 : INFO : utils.util : After clean up. 4 items remained 2021-11-18 06:15:22,981 : INFO : utils.util : Before clean up. 4 items in total 2021-11-18 06:15:23,215 : INFO : utils.util : After clean up. 1 items remained 2021-11-18 06:15:36,117 : INFO : utils.util : Before clean up. 1 items in total 2021-11-18 06:15:36,164 : INFO : utils.util : After clean up. 0 items remained
处理后的邮件数量变化为:335->167->83->41->20->9->4->1->0,符合每次减半的规律。
初始代码
import os import win32com.client def get_target_folder(folder: str): outlook = win32com.client.Dispatch('outlook.application') mapi = outlook.GetNamespace("MAPI") target = mapi dir = folder.split("\\") for d in dir: try: target = target.Folders(d) except: logger.error("Current folder path {}. The sub folder {} doesn't exist".format(target.FolderPath, d)) target = None break return target def save_job_status(): mailfolder= config["OUTLOOK"]["JOBS"] keyword = config["OUTLOOK"]["KEYWORD"] destination = config["OUTLOOK"]["LOCALDATA"] criteria = f"@SQL=\"urn:schemas:httpmail:subject\" like '%{keyword}%'" folder = get_target_folder(folder=mailfolder) items = folder.items emails = items.restrict(criteria) logger.info("Before clean up. {} items in total".format(emails.count)) for email in emails: try: attachments = email.attachments for attachment in attachments: filename = attachment.FileName attachment.SaveAsFile(os.path.join(destination, filename )) email.Delete() except: logger.error("Can't operate on the email {}".format(email.Subject)) items = folder.items logger.info("After clean up. {} items remained".format(items.count))
额外说明:我提前设置了Outlook规则,所有符合条件的待处理邮件都会自动进入
JOBS文件夹,代码中的criteria过滤条件是为了避免误操作额外添加的。
问题根源
核心问题是遍历集合的同时对集合本身执行删除操作。
当你用for email in emails遍历Outlook的Items集合时,迭代器会按顺序访问索引0、1、2...的元素:
- 当你删除索引0的元素后,原索引1的元素会自动前移成为新的索引0,原索引2的元素成为新的索引1,以此类推
- 下一次迭代时,迭代器会访问索引1的元素,等于直接跳过了原本的索引1(现在的索引0)的邮件,所以每处理1封就会跳过1封,最终表现为每次处理后剩余一半左右的邮件
解决方案
将正序遍历集合改为倒序索引访问,从最大索引值向最小索引值遍历,删除末尾元素不会影响前面元素的索引值,修改后的核心代码如下:
count = emails.count for i in range(count-1, -1, -1): print("{} {}".format(i, emails[i].subject)) # 保存附件逻辑不变 attachments = emails[i].attachments for attachment in attachments: filename = attachment.FileName attachment.SaveAsFile(os.path.join(destination, filename )) emails[i].Delete()
内容的提问来源于stack exchange,提问作者Ginger_Chacha
相关产品推荐
相关产品推荐

