You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python连接Outlook遇AttributeError及附件提取不全问题求助

问题

我编写了一段Python代码,要实现以下功能:

  • 连接Outlook并指定子文件夹
  • 获取用户输入的目标日期
  • 搜索该日期内带有report.csv附件的邮件
  • 移除邮件主题中的特定关键词
  • 提取附件
  • 以处理后的邮件主题为文件名保存附件到指定文件夹

代码虽能运行,但仅提取30余份报告中的4份就崩溃,VS Code提示错误出在第54行outlook.GetNamespace.Quit(),错误信息为AttributeError: GetNamespace.GetNamespace。附上代码求问题分析及解决思路:

import os
import win32com.client
import datetime as dt
from datetime import date, timedelta
from pathlib import Path

EMAIL_ADDRESS = "emailaddress@example.com"
FOLDER_NAME = "subfolder_name"
OUTPUT_FOLDER = Path(r"Specify Path")   #specify the path

def connect_to_outlook():
    outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace ("MAPI")
    inbox_folder = outlook.GetDefaultFolder(6)  # Inbox folder
    sub_folder = inbox_folder.Folders(FOLDER_NAME)
    return sub_folder, outlook, inbox_folder, outlook

def extract_csv_attachments(emails):
    csv_attachments = [(email.Subject.split("[INSERT TEXT TO PARSE/SPLIT")[0].strip(), attachment)
                       for email in emails
                       for attachment in email.Attachments if attachment.FileName == "report.csv"]
    return csv_attachments

try:
    # Connect to Outlook
    sub_folder, outlook, inbox_folder, outlook = connect_to_outlook()

    # Get target date from user input
    target_date_str = input("Enter a target date (YYYY-MM-DD): ")
    target_date = dt.datetime.strptime(target_date_str, '%Y-%m-%d')

    # Construct search criteria and filter emails
    search_criteria = f'[SenderEmailAddress] = "{EMAIL_ADDRESS}" AND [ReceivedTime] >= "{target_date.strftime("%m/%d/%Y")}" AND [ReceivedTime] < "{(target_date + dt.timedelta(days=1)).strftime("%m/%d/%Y")}"'
    filtered_emails = sub_folder.Items.Restrict(search_criteria)
    

    # Extract CSV attachments from filtered emails
    csv_attachments = extract_csv_attachments(filtered_emails)

    # Save extracted attachments to output folder
    for email_subject, attachment in csv_attachments:
        attachment.SaveAsFile(OUTPUT_FOLDER / f"{email_subject}.csv")

finally:
    # Release Outlook resources
    sub_folder = None
    inbox_folder = None
    outlook.GetNamespace.Quit()
    outlook.Quit()
问题分析
  1. 核心错误:Outlook对象调用逻辑混乱
    outlook变量本身是GetNamespace("MAPI")返回的命名空间对象,而非Outlook应用实例。调用outlook.GetNamespace.Quit()完全错误——命名空间对象不存在GetNamespace属性,直接触发AttributeError。
  2. 重复赋值导致对象指向混乱
    • connect_to_outlook函数返回值重复写了outlook,导致inbox_folder被覆盖为命名空间对象:return sub_folder, outlook, inbox_folder, outlook
    • 调用函数时再次重复赋值:sub_folder, outlook, inbox_folder, outlook = connect_to_outlook(),进一步打乱变量指向,可能提前释放关键资源导致中途崩溃。
  3. 遍历与资源处理的潜在问题
    直接遍历Restrict过滤后的Items集合,未处理COM对象引用泄漏;资源释放逻辑错误,加剧Outlook进程异常风险。
解决思路与修正代码
  1. 修正对象引用与资源释放
    • 明确区分Outlook.Application实例和MAPI命名空间对象,仅对Application实例调用Quit()
    • 清理重复赋值错误,确保每个变量指向正确对象
  2. 优化稳定性与鲁棒性
    • 处理主题分割时的边界情况,避免索引错误
    • 对过滤后的邮件排序,避免遍历异常
    • 处理文件名非法字符,防止保存失败

修正后的代码:

import os
import win32com.client
import datetime as dt
from datetime import timedelta
from pathlib import Path

EMAIL_ADDRESS = "emailaddress@example.com"
FOLDER_NAME = "subfolder_name"
OUTPUT_FOLDER = Path(r"Specify Path")   # 指定保存路径

def connect_to_outlook():
    # 分离Outlook应用实例与MAPI命名空间
    outlook_app = win32com.client.Dispatch("Outlook.Application")
    mapi_namespace = outlook_app.GetNamespace("MAPI")
    inbox_folder = mapi_namespace.GetDefaultFolder(6)  # 6代表收件箱
    sub_folder = inbox_folder.Folders(FOLDER_NAME)
    # 返回正确的对象集合
    return sub_folder, mapi_namespace, inbox_folder, outlook_app

def extract_csv_attachments(emails):
    csv_attachments = []
    for email in emails:
        # 处理主题分割的边界情况
        keyword = "[INSERT TEXT TO PARSE/SPLIT]"
        cleaned_subject = email.Subject.split(keyword)[0].strip() if keyword in email.Subject else email.Subject.strip()
        for attachment in email.Attachments:
            if attachment.FileName == "report.csv":
                csv_attachments.append((cleaned_subject, attachment))
    return csv_attachments

try:
    # 获取正确的对象引用
    sub_folder, mapi_namespace, inbox_folder, outlook_app = connect_to_outlook()

    # 获取目标日期
    target_date_str = input("输入目标日期(YYYY-MM-DD):")
    target_date = dt.datetime.strptime(target_date_str, '%Y-%m-%d')

    # 构造更精确的搜索条件(包含时间)
    start_date = target_date.strftime("%m/%d/%Y 00:00 AM")
    end_date = (target_date + timedelta(days=1)).strftime("%m/%d/%Y 00:00 AM")
    search_criteria = f'[SenderEmailAddress] = "{EMAIL_ADDRESS}" AND [ReceivedTime] >= "{start_date}" AND [ReceivedTime] < "{end_date}"'
    
    filtered_emails = sub_folder.Items.Restrict(search_criteria)
    # 排序避免遍历异常
    filtered_emails.Sort("[ReceivedTime]", win32com.client.constants.olAscending)

    # 提取并保存附件
    csv_attachments = extract_csv_attachments(filtered_emails)
    for email_subject, attachment in csv_attachments:
        save_path = OUTPUT_FOLDER / f"{email_subject}.csv"
        # 替换文件名中的非法字符
        save_path = save_path.with_name(save_path.name.replace('/', '_').replace('\\', '_').replace(':', '_'))
        attachment.SaveAsFile(str(save_path))
        print(f"已保存:{save_path}")

finally:
    # 正确释放资源:先置空子对象,再退出应用
    sub_folder = None
    inbox_folder = None
    mapi_namespace = None
    if 'outlook_app' in locals():
        outlook_app.Quit()

内容的提问来源于stack exchange,提问作者CyberCoder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 17:17:48