上周财务报销季,我被 20 多封报销邮件搞到崩溃 —— 每个同事发的邮件里都带一个 Excel 报销单,需要手动下载附件、打开核对 “报销项目、金额、发票号”,再复制到总表。光是下载附件就花了半小时,还因为漏看了 3 封邮件,导致同事的报销延迟提交,被追着问了好几天。​

后来想起 Python 能操作邮件和 Excel,花了 1 小时写了段代码,没想到自动下载所有附件、提取关键信息、生成汇总表一步到位,全程只用了 2 分钟。现在把这个实战案例分享出来,不管是财务还是行政,只要会复制代码,就能告别手动处理邮件附件的麻烦。​

一、先明确问题:我们要解决什么报销处理痛点?​

每月报销季,同事会通过企业邮箱发送报销邮件,每封邮件含 1 个 Excel 附件(命名格式:“张三_9 月报销.xlsx”“李四_9 月报销.xlsx”),附件里有 “报销项目、金额、发票号、报销日期”4 列数据,需要完成 3 件事:​

  1. 批量下载附件:自动识别报销邮件,下载所有 Excel 附件到指定文件夹;​
  1. 提取关键信息:从每个附件中提取 “姓名、报销项目、金额、发票号”(姓名从附件名获取);​
  1. 生成汇总表:把所有数据汇总到一个 Excel,方便核对和审批。​

手动处理时,不仅要逐封邮件下载附件,还要担心漏看或录错数据;用 Python 的话,依赖imaplib(操作邮件)、pandas(处理 Excel)两个库,就能全程自动化。​

二、准备工作:5 分钟搭好环境​

首先安装所需 Python 库,打开 “命令提示符”(Windows)或 “终端”(Mac),输入以下命令:​

pip install pandas openpyxl imaplib2
  • pandas+openpyxl:用于读取 Excel 附件、生成汇总表;​
  • imaplib2:支持连接企业邮箱,读取邮件和附件(比自带imaplib更稳定)。​

然后提前准备 3 个关键信息(后续代码要用到):​

  1. 企业邮箱地址(如 “finance@company.com”);​
  1. 邮箱授权码(不是登录密码!在邮箱设置里开启 “IMAP 服务” 后获取,不同邮箱获取方式不同,比如网易邮箱在 “账户安全” 里申请);​
  1. 邮件主题关键词(如 “9 月报销”,确保能精准识别报销邮件,避免下载其他附件)。​

三、代码实现:分 3 步搞定报销附件处理​

第一步:连接邮箱,批量下载报销附件​

先通过 IMAP 协议连接企业邮箱,筛选出含 “9 月报销” 关键词的邮件,自动下载附件到指定文件夹。​

代码带详细注释,新手也能看懂:​

# 导入需要的库
import imaplib
import email
from email.header import decode_header
import os
import pandas as pd

# 1. 连接邮箱,下载报销附件
# 邮箱配置信息(替换成你的实际信息)
email_user = "finance@company.com"  # 你的邮箱地址
email_password = "abc123def456"  # 邮箱授权码(不是登录密码)
imap_server = "imap.163.com"  # 邮箱IMAP服务器(网易是imap.163.com,腾讯是imap.qq.com)
attachment_save_path = "D:\\9月报销附件"  # 附件保存路径
subject_keyword = "9月报销"  # 报销邮件主题关键词

# 创建附件保存文件夹(如果不存在)
if not os.path.exists(attachment_save_path):
    os.makedirs(attachment_save_path)

# 连接IMAP服务器
mail = imaplib.IMAP4_SSL(imap_server)
mail.login(email_user, email_password)
mail.select("inbox")  # 选择收件箱

# 搜索含关键词的邮件(UTF-8编码,避免中文乱码)
keyword = f'"*{subject_keyword}*"'
status, data = mail.search(None, 'SUBJECT', keyword.encode('utf-8'))
email_ids = data[0].split()  # 获取所有符合条件的邮件ID

# 遍历邮件,下载附件
for email_id in email_ids:
    status, msg_data = mail.fetch(email_id, "(RFC822)")
    msg = email.message_from_bytes(msg_data[0][1])
    
    # 解码邮件主题(避免中文乱码)
    subject = decode_header(msg["Subject"])[0][0]
    if isinstance(subject, bytes):
        subject = subject.decode("utf-8")
    
    # 遍历邮件附件
    for part in msg.walk():
        # 判断是否为附件
        if part.get_content_disposition() == "attachment":
            # 解码附件名称
            filename = decode_header(part.get_filename())[0][0]
            if isinstance(filename, bytes):
                filename = filename.decode("utf-8")
            
            # 只保存Excel附件(避免下载其他格式文件)
            if filename.endswith(".xlsx"):
                # 保存附件到指定路径
                attachment_path = os.path.join(attachment_save_path, filename)
                with open(attachment_path, "wb") as f:
                    f.write(part.get_payload(decode=True))
                print(f"已下载附件:{filename}")

# 关闭邮箱连接
mail.logout()
print(f"\n所有报销附件已保存到:{attachment_save_path}")

运行这段代码后,打开 “D:\9 月报销附件” 文件夹,会看到所有同事的 Excel 报销单已自动下载,不用再逐封邮件点击 “下载附件”。​

第二步:提取附件关键信息,合并成总数据​

接着读取所有下载的 Excel 附件,提取 “姓名(从文件名获取)、报销项目、金额、发票号”,合并成一个总 DataFrame。​

代码继续往下写:​

# 2. 提取附件信息,合并数据
all_reimbursement = []  # 存放所有报销数据

# 遍历附件文件夹里的Excel文件
for filename in os.listdir(attachment_save_path):
    if filename.endswith(".xlsx"):
        # 从文件名提取员工姓名(如“张三_9月报销.xlsx”提取“张三”)
        employee_name = filename.split("_")[0]
        # 读取Excel附件(假设数据在第一个工作表,列名是“报销项目、金额、发票号、报销日期”)
        file_path = os.path.join(attachment_save_path, filename)
        df = pd.read_excel(file_path, engine="openpyxl")
        
        # 添加“员工姓名”列,方便后续汇总
        df["员工姓名"] = employee_name
        # 只保留需要的列(避免无关数据干扰)
        df = df[["员工姓名", "报销项目", "金额", "发票号"]]
        
        # 处理可能的空值(金额为空的行跳过)
        df = df.dropna(subset=["金额"])
        # 确保金额是数值类型(避免后续汇总出错)
        df["金额"] = pd.to_numeric(df["金额"], errors="coerce").fillna(0)
        
        all_reimbursement.append(df)

# 合并所有报销数据
total_reimbursement = pd.concat(all_reimbursement, ignore_index=True)
# 按员工姓名分组,计算每人总报销金额(可选,用于快速核对)
employee_total = total_reimbursement.groupby("员工姓名")["金额"].sum().reset_index()
employee_total.columns = ["员工姓名", "总报销金额"]

print("\n各员工总报销金额:")
print(employee_total)
print(f"\n所有报销数据共{len(total_reimbursement)}行")

运行后,控制台会显示每个员工的总报销金额,比如 “张三 总报销金额 1280 元”,还能看到总数据行数,方便快速核对是否有遗漏。​

第三步:生成带格式的报销汇总表​

最后把合并后的数据保存成 Excel 汇总表,加个简单格式(表头加粗、调整列宽),方便财务审批。​

代码如下:​

# 3. 生成报销汇总Excel表
summary_save_path = os.path.join(attachment_save_path, "9月报销汇总表.xlsx")

# 使用ExcelWriter添加格式
with pd.ExcelWriter(summary_save_path, engine="openpyxl") as writer:
    # 写入“所有报销明细”工作表
    total_reimbursement.to_excel(writer, sheet_name="报销明细", index=False)
    # 写入“员工总报销”工作表
    employee_total.to_excel(writer, sheet_name="员工总报销", index=False)
    
    # 给“报销明细”表加格式
    worksheet1 = writer.sheets["报销明细"]
    # 调整列宽
    worksheet1.column_dimensions["A"].width = 12  # 员工姓名
    worksheet1.column_dimensions["B"].width = 20  # 报销项目
    worksheet1.column_dimensions["C"].width = 10  # 金额
    worksheet1.column_dimensions["D"].width = 15  # 发票号
    # 表头加粗
    from openpyxl.styles import Font
    header_font = Font(bold=True)
    for col in range(1, 5):
        worksheet1.cell(row=1, column=col).font = header_font
    
    # 给“员工总报销”表加格式
    worksheet2 = writer.sheets["员工总报销"]
    worksheet2.column_dimensions["A"].width = 12
    worksheet2.column_dimensions["B"].width = 15
    for col in range(1, 3):
        worksheet2.cell(row=1, column=col).font = header_font

print(f"\n报销汇总表已保存到:{summary_save_path}")

打开生成的 “9 月报销汇总表.xlsx”,会看到两个工作表:“报销明细”(所有报销项目的详细数据)和 “员工总报销”(每人总金额),格式工整,直接打印或发审批群都没问题。我上次生成后,财务同事说 “比手动汇总的表清楚多了,还不用核对数据”。

Logo

葡萄城是专业的软件开发技术和低代码平台提供商,聚焦软件开发技术,以“赋能开发者”为使命,致力于通过表格控件、低代码和BI等各类软件开发工具和服务

更多推荐