在日常办公中,经常需要从一个包含多个工作表的Excel工作簿中,按同一条件筛选数据(例如只保留“销售区域=华东”的记录)。手动操作需要逐表筛选、复制、粘贴,不仅繁琐而且容易遗漏。本文介绍一种基于pandas和xlwings的自动化方案,能稳定地遍历工作簿中所有工作表,按指定字段和值进行筛选,并将结果写入新的工作簿。
适用场景与前置检查
此脚本适用于多个工作表结构相似(表头在第一行、字段名一致、数据连续),且需按同一个字段过滤的场景,例如多月份销售表或区域明细表。
运行前务必确认三点:表头是否在第一行、筛选字段名称是否在所有工作表中统一、数据区域是否连续无空行。若不符合,脚本需额外兼容处理。
核心原理与流程
流程可拆解为五步:打开源工作簿 → 遍历每张工作表 → 利用pandas读取为DataFrame → 按条件过滤数据 → 将结果写入新建的工作簿。
pandas擅长数据筛选与操作,xlwings负责与Excel文件直接交互(打开、读取、写入、保存),两者配合比模拟鼠标点击更稳定。
完整代码实现
以下代码展示一个典型场景:从“销售明细.xlsx”中所有工作表里提取“销售区域”为“华东”的数据,生成新文件“筛选结果_华东.xlsx”。- import os
- import re
- import pandas as pd
- import xlwings as xw
- def safe_sheet_name(name):
- """清理工作表名称,避免超过Excel限制或包含非法字符"""
- name = str(name).strip()
- name = re.sub(r'[\\/:*?\[\]]', "_", name)
- return name[:31] if name else "筛选结果"
- # 请根据实际环境修改以下参数
- source_file = r"E:\example\销售明细.xlsx"
- result_file = r"E:\example\筛选结果_华东.xlsx"
- filter_column = "销售区域"
- filter_value = "华东"
- app = xw.App(visible=False, add_book=False)
- try:
- wb = app.books.open(source_file)
- result_wb = app.books.add()
-
- # 删除默认多余工作表,保留第一张备用
- while len(result_wb.sheets) > 1:
- result_wb.sheets[-1].delete()
-
- output_count = 0
- for sht in wb.sheets:
- print(f"正在处理工作表:{sht.name}")
- try:
- data = sht.range("A1").options(
- pd.DataFrame,
- header=1,
- index=False,
- expand="table"
- ).value
- except Exception as e:
- print(f"跳过:{sht.name},读取失败:{e}")
- continue
-
- if data is None or data.empty:
- print(f"跳过:{sht.name},空表或无有效数据")
- continue
-
- # 清理字段名前后空格
- data.columns = [str(col).strip() for col in data.columns]
-
- if filter_column not in data.columns:
- print(f"跳过:{sht.name},缺少字段:{filter_column}")
- continue
-
- # 条件筛选
- result = data[data[filter_column].astype(str).str.strip() == filter_value]
- if result.empty:
- print(f"未命中:{sht.name},没有符合条件的数据")
- continue
-
- # 写入结果工作簿
- if output_count == 0:
- result_sht = result_wb.sheets[0]
- result_sht.name = safe_sheet_name(sht.name)
- else:
- result_sht = result_wb.sheets.add(name=safe_sheet_name(sht.name), after=result_wb.sheets[-1])
- result_sht.range("A1").value = result
- result_sht.autofit()
- output_count += 1
- print(f"已写入:{sht.name},筛选结果 {len(result)} 行")
-
- wb.close()
- if output_count == 0:
- print("没有任何工作表筛选出结果,结果文件未保存。")
- result_wb.close()
- else:
- result_wb.save(result_file)
- result_wb.close()
- print(f"筛选完成:{result_file}")
- finally:
- app.quit()
复制代码
关键判断:为什么必须先检查字段存在性
批量处理时,不能默认所有工作表字段完全规范。如果某张表缺少目标字段或字段名有差异,直接使用data[filter_column]会引发异常。代码中通过if filter_column not in data.columns提前判断,可跳过并输出日志,确保脚本不被中断。
若数据来源混乱,可维护一个字段别名列表(如[“销售区域”,“区域”,“所属区域”]),逐个匹配。但需注意别名不能跨语义字段使用,以免误判。
效果验证与数据对账
脚本运行后,不能只看文件是否生成。建议至少检查:结果文件能否正常打开、每个工作表是否存在、筛选字段值是否全部为目标值(可用代码检查)、行数与手工筛选结果是否一致、关键字段是否完整。
验证代码片段(可放在脚本最后):- # 验证某个结果表中的筛选字段是否全部符合条件
- check_result = result[filter_column].astype(str).str.strip().eq(filter_value).all()
- if check_result:
- print(f"{sht.name} 验证通过:筛选结果全部为 {filter_value}")
- else:
- print(f"{sht.name} 验证失败:存在不符合条件的数据")
复制代码
常见踩坑与处理经验
1. 字段名隐藏空格:表头可能带有不可见空格,使用strip()清理字段名和数据值可解决。
2. 筛选值不统一:如“华东”“华东区”“华东区域”被视为不同值,需在筛选前做标准化替换。
3. 工作表名称超长:Excel限制工作表名不超过31字符,safe_sheet_name()做了截断和非法字符替换。
4. 结果文件被占用:运行脚本前关闭源文件和结果文件,否则保存会失败。
5. 默认空白工作表:新建工作簿自带空白表,应删除多余表或复用第一张表,避免结果文件出现无用空表。
总结与扩展建议
本方案的核心模式是“打开工作簿→遍历工作表→读取DataFrame→条件筛选→写入结果”,适用于结构一致的多表批量过滤。值得保留的经验:字段校验比筛选语法更重要;结果验证不能省略;稳定性高于代码简洁。
后续可将源文件路径、筛选条件、字段别名等参数化,或封装成窗口工具,使其成为可复用的自动化组件。 |