Python自动化办公实例:批量重命名文件与Excel数据处理脚本

Aria
Aria
发布时间:2026-11-12| 12人浏览过
原创

本文提供基于Python标准库pathlib的文件批量重命名脚本,以及基于pandas的多Excel工作簿自动化合并清洗脚本,包含完整代码、防覆盖冲突机制与数据校验流程。

分类:Python实战教程 核心库:pathlib / pandas / openpyxl 适用场景:日常文件整理与多表格汇总
Python自动化办公实例:批量重命名文件与Excel数据处理脚本
在日常办公中,面对成百上千个名称杂乱的外部文件和格式相似的分散Excel表格,逐个重命名和手动复制汇总不仅耗时长,而且极易出现漏统计、误覆盖或数据格式错误。利用Python标准库中的pathlib模块可以构建零依赖、跨平台的文件重命名方案,配合pandas与openpyxl库则能快速实现数十个Excel文件的自动对齐、清洗与合并。本文直接提供这两个典型场景下的完整可执行脚本,详细拆解规则设计、冲突处理与结果校验步骤。

一、运行环境准备与脚本依赖

完成本文任务需要Python 3.8或更高版本。批量重命名脚本完全基于Python标准库,无需额外安装模块;Excel批处理脚本采用数据分析领域通用的pandas与底层的openpyxl引擎进行表格读写。

在开始编写代码前,通过终端或命令行安装所需依赖:

pip install pandas openpyxl

为了保证脚本执行时路径明确且互不干扰,建议建立如下目录结构。输入文件和输出结果分开存储,既方便查看效果,也能防止误修改原始素材:

project_workspace/
│
├── batch_rename.py       # 文件批量重命名脚本
├── process_excel.py      # Excel合并清洗脚本
│
├── raw_files/            # 待重命名文件存放目录
│   ├── invoice_scan_01.pdf
│   └── memo_temp.docx
│
└── raw_excels/           # 待汇总的Excel报表存放目录
    ├── 2024_01_华东区销售.xlsx
    ├── 2024_01_华北区销售.xlsx
    └── output/           # 汇总导出文件目录
Python自动化办公实例:批量重命名文件与Excel数据处理脚本
图 1:Python自动化办公双引擎体系架构与数据处理流程

二、实例一:批量重命名文件脚本实战

重命名文件常见的问题是命名逻辑混乱、误覆盖同名文件以及缺乏执行前的预演。使用pathlib.Path处理路径比传统的os.path更面向对象,支持直接提取文件的纯文件名(stem)与后缀名(suffix)。

1. 重命名规则设计与安全防范

本实例的目标是将指定目录下的杂乱文件统一规范为:[业务前缀]_[修改日期]_[原文件名或自增编号].[原扩展名]。为了防止由于重名导致旧文件被静默覆盖,脚本在重命名阶段执行目标存在性检测。如果目标文件已存在,则自动添加自增数字后缀进行消歧;同时内置dry_run模拟执行参数,首次运行仅打印改名计划,核对无误后再实际写入磁盘。

Python自动化办公实例:批量重命名文件与Excel数据处理脚本
图 2:批量重命名防覆盖安全检测与执行逻辑

2. 批量重命名完整代码实现

创建 batch_rename.py 脚本,并将目标文件夹路径传入 batch_rename_files 函数:

import datetime
from pathlib import Path


def batch_rename_files(
    target_dir: str,
    prefix: str = "DOC",
    include_date: bool = True,
    dry_run: bool = True,
) -> None:
    """批量重命名指定目录下的文件

    :param target_dir: 待处理的目标文件夹绝对或相对路径
    :param prefix: 重命名后的业务前缀,如 INV, REPORT, CONTRACT
    :param include_date: 是否在文件名中加入最后修改日期
    :param dry_run: 为True时仅打印预览,不实际修改文件;确认无误后改为False执行
    """
    folder = Path(target_dir)
    if not folder.exists() or not folder.is_dir():
        print(f"[错误] 目标目录不存在或非有效文件夹: {target_dir}")
        return

    # 按文件创建/修改时间正向排序,确保命名序号相对稳定
    files = [f for f in folder.iterdir() if f.is_file() and not f.name.startswith(".")]
    files.sort(key=lambda item: item.stat().st_mtime)

    print(f"[*] 发现待处理文件数: {len(files)} (dry_run 模式: {dry_run})")
    print("-" * 65)

    renamed_count = 0
    for idx, file_path in enumerate(files, start=1):
        suffix = file_path.suffix.lower()
        parts = [prefix]

        if include_date:
            mtime = file_path.stat().st_mtime
            date_str = datetime.datetime.fromtimestamp(mtime).strftime("%Y%m%d")
            parts.append(date_str)

        # 构造基准新名称,如 DOC_20241030_001.pdf
        base_name = f"{'_'.join(parts)}_{idx:03d}"
        new_filename = f"{base_name}{suffix}"
        target_path = folder / new_filename

        # 安全防覆盖:如果目标文件已存在且不是自己,添加碰撞消歧后缀
        collision_idx = 1
        while target_path.exists() and target_path != file_path:
            target_path = folder / f"{base_name}_{collision_idx}{suffix}"
            collision_idx += 1

        if target_path == file_path:
            print(f"[跳过] 命名已符合规范: {file_path.name}")
            continue

        if dry_run:
            print(f"[预演] {file_path.name} -> {target_path.name}")
        else:
            file_path.rename(target_path)
            print(f"[完成] {file_path.name} -> {target_path.name}")
            renamed_count += 1

    print("-" * 65)
    if dry_run:
        print("[提示] 预演完毕,未写入磁盘。如确认无误,请传入 dry_run=False 正式重命名。")
    else:
        print(f"[成功] 批量重命名完成,共修改 {renamed_count} 个文件。")


if __name__ == "__main__":
    # 执行示例:首次运行保持 dry_run=True 进行预演
    target_folder = "./raw_files"
    batch_rename_files(
        target_dir=target_folder,
        prefix="REPORT",
        include_date=True,
        dry_run=True,
    )
注意事项: 在Windows系统中,如果文件正被其他应用程序(如WPS、Acrobat Reader或Office)独占打开,执行 rename() 会抛出 PermissionError。建议在执行脚本前关闭所有相关软件。

3. 执行结果示例输出

将 dry_run 设置为 True 时,终端输出如下所示:

示例运行终端输出 (dry_run=True)
[*] 发现待处理文件数: 3 (dry_run 模式: True) ----------------------------------------------------------------- [预演] memo_temp.docx -> REPORT_20241029_001.docx [预演] invoice_scan_01.pdf -> REPORT_20241030_002.pdf [预演] draft_v2.pdf -> REPORT_20241030_003.pdf ----------------------------------------------------------------- [提示] 预演完毕,未写入磁盘。如确认无误,请传入 dry_run=False 正式重命名。

三、实例二:多Excel文件合并与数据清洗脚本

在跨部门协作或定期统计中,各分支机构常上报单独的Excel表格。这些表格往往存在几类普遍问题:

  • 空行与脏数据:表格末尾常有空白行,或由于编辑不慎遗留的空格符。
  • 临时小计/总计干扰:各分表自带“本页小计”或“汇总”行,若直接简单合并会导致统计总数翻倍。
  • 来源信息缺失:合并后若不额外补充一列“数据归属文件”,将无法追溯每条记录的具体部门或发生地。
Python自动化办公实例:批量重命名文件与Excel数据处理脚本
图 3:多Excel工作簿提取、清洗与合并数据流模型

1. 数据合并与清洗核心代码

编写 process_excel.py 脚本,支持批量检索指定文件夹下的所有 .xlsx 文件,并进行针对性清洗:

from pathlib import Path
from typing import List
import pandas as pd


def clean_single_dataframe(df: pd.DataFrame, source_filename: str) -> pd.DataFrame:
    """对单个Excel读取出的DataFrame进行通用清洗"""
    # 1. 剔除全空行与全空列
    df = df.dropna(how="all").dropna(axis=1, how="all")

    if df.empty:
        return df

    # 2. 规范列名:去除首尾空格,统一转为字符串
    df.columns = [str(col).strip() for col in df.columns]

    # 3. 过滤典型小计/合计行(针对首列包含'合计'或'小计'关键词的记录)
    first_col = df.columns[0]
    df = df[~df[first_col].astype(str).str.contains("小计|合计|总计", na=False)]

    # 4. 关键类型规范化示例:如果存在'销售金额'或'数量'列,转换为数值型,非数值设为0
    for num_col in ["销售金额", "金额", "数量"]:
        if num_col in df.columns:
            df[num_col] = pd.to_numeric(df[num_col], errors="coerce").fillna(0.0)

    # 5. 关键日期列规范化示例:统一为 YYYY-MM-DD
    for date_col in ["日期", "下单时间", "销售日期"]:
        if date_col in df.columns:
            df[date_col] = pd.to_datetime(df[date_col], errors="coerce").dt.strftime("%Y-%m-%d")

    # 6. 追加来源标签列,便于排查数据源头
    df["数据来源"] = source_filename

    return df


def merge_excel_files(input_dir: str, output_file: str, sheet_name: str = 0) -> None:
    """批量合并目录下所有Excel文件,并输出至指定目标

    :param input_dir: 包含待合并Excel文件的目录
    :param output_file: 导出的合并总表路径
    :param sheet_name: 读取的Sheet名称或索引,默认读取第1张Sheet
    """
    source_folder = Path(input_dir)
    if not source_folder.exists() or not source_folder.is_dir():
        print(f"[错误] 目录不存在: {input_dir}")
        return

    # 过滤掉以 ~$ 开头的Excel临时锁定文件
    excel_files: List[Path] = [
        f for f in source_folder.glob("*.xlsx")
        if not f.name.startswith("~$")
    ]

    if not excel_files:
        print("[提示] 指定目录下未发现任何可合并的 .xlsx 文件。")
        return

    print(f"[*] 找到 {len(excel_files)} 个Excel文件,开始提取与清洗...")
    print("-" * 65)

    data_frames = []
    total_raw_rows = 0

    for file_path in excel_files:
        try:
            # 使用 openpyxl 引擎读取
            raw_df = pd.read_excel(file_path, sheet_name=sheet_name, engine="openpyxl")
            total_raw_rows += len(raw_df)

            cleaned_df = clean_single_dataframe(raw_df, file_path.name)
            if not cleaned_df.empty:
                data_frames.append(cleaned_df)
                print(f"[读取] {file_path.name}: 原始 {len(raw_df)} 行 -> 清洗后 {len(cleaned_df)} 行")
            else:
                print(f"[跳过] {file_path.name}: 数据清洗后为空表格")
        except Exception as err:
            print(f"[警告] 读取文件失败 {file_path.name}: {err}")

    if not data_frames:
        print("[错误] 未能成功提取任何有效数据。")
        return

    # 纵向堆叠合并(对齐列名,缺失列自动填充为 NaN)
    master_df = pd.concat(data_frames, ignore_index=True)

    # 确保输出目录存在
    out_path = Path(output_file)
    out_path.parent.mkdir(parents=True, exist_ok=True)

    # 导出至Excel,不保留Pandas自带的数字索引
    master_df.to_excel(out_path, index=False, engine="openpyxl")

    print("-" * 65)
    print(f"[完成] 所有表格合并完毕!")
    print(f"  • 输入文件总数: {len(excel_files)}")
    print(f"  • 原始记录总行数: {total_raw_rows}")
    print(f"  • 清洗后最终行数: {len(master_df)}")
    print(f"  • 结果已保存至: {out_path.resolve()}")


if __name__ == "__main__":
    input_folder = "./raw_excels"
    target_output = "./raw_excels/output/2024年度销售数据总汇.xlsx"
    merge_excel_files(input_dir=input_folder, output_file=target_output)
特别提示:临时锁定文件过滤
当某个Excel文件在WPS或Microsoft Excel中处于打开状态时,操作系统会在同一目录下生成一个隐藏的以 ~$ 开头的同名临时文件(例如 ~$2024_01_华东区销售.xlsx)。如果不进行显式过滤,Pandas尝试读取它时会直接抛出 BadZipFile 错误导致脚本中断。代码中使用 if not f.name.startswith("~$") 是关键防护点。

2. 运行结果示例展示

示例运行终端输出
[*] 找到 3 个Excel文件,开始提取与清洗... ----------------------------------------------------------------- [读取] 2024_01_华东区销售.xlsx: 原始 121 行 -> 清洗后 118 行 [读取] 2024_01_华北区销售.xlsx: 原始 98 行 -> 清洗后 95 行 [读取] 2024_01_华南区销售.xlsx: 原始 85 行 -> 清洗后 80 行 ----------------------------------------------------------------- [完成] 所有表格合并完毕! • 输入文件总数: 3 • 原始记录总行数: 304 • 清洗后最终行数: 293 • 结果已保存至: /workspace/raw_excels/output/2024年度销售数据总汇.xlsx

四、自动化办公工作流串联与异常防护

重命名与Excel批处理不仅可以独立执行,还可以组合为自动化流水线:先通过重命名脚本把外部上报的任意命名报表按统一命名规范重新归档,随后批处理脚本直接根据归一化后的名称进行正则过滤与批量合并,形成完整闭环。

Python自动化办公实例:批量重命名文件与Excel数据处理脚本
图 4:自动化脚本方案与传统手工操作关键维度效能对比

五、常见问题排查与优化建议

常见现象 / 报错 根本原因 针对性解决办法
PermissionError: [WinError 32] 文件被另一个进程占用(如Excel窗口处于打开状态) 关闭被打开的Excel或PDF文件后再运行脚本;在代码中捕获该异常跳过或打印提示
BadZipFile: File is not a zip file 读取到了以 ~$ 开头的临时锁文件,或者文件损坏、非真xlsx格式 严格在文件遍历中增加 not f.name.startswith("~$") 条件过滤
合并后数字列显示为 0.0 或空白 原Excel中存在千分位逗号(如 "1,234.50")导致转数值失败 先执行 str.replace(",", "") 去掉逗号,再调用 pd.to_numeric()
日期列出现 1970-01-01 或时间戳数字 Excel中的数字型序列日期未被解析,或者单元格格式为纯文本 使用 pd.to_datetime(df['日期'], origin='1899-12-30', unit='D') 进行Excel序列号转换

通过合理配置pathlib进行原子性安全操作,结合pandas的向量化清洗能力,即可将以往耗费大量人力的文件归档与表格汇总工作固化为标准化脚本。在实际工程落地时,建议始终先在测试目录下使用少量样本进行预演校验,确认清洗规则覆盖所有异常分支后,再批量投入日常办公环境。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。