2026-10-06 · 喵窝资源铺
「客户 A 的订单到底记在哪个表里?」——文件名五花八门(9月订单.xlsx、客户台账、导出表…),每个里面还可能多个 sheet,Ctrl+F 开一个查一个,50 个文件查到怀疑人生。这篇给一个 openpyxl 脚本:扫整个文件夹的所有 xlsx,按关键字搜每个 sheet 的表头和全文,命中的行连同来源文件名、工作表名、匹配方式一起汇总到一个结果表。代码实跑验证,真实输出贴在下面。
构造 5 个文件的测试目录,故意埋了公式表、坏文件、单列表、表头前有说明行的表,搜关键字「喵喵」:
扫描 5 个文件,命中 2 行,结果已写入 汇总.xlsx 跳过: 坏文件.xlsx: 打不开(File is not a zip file) 跳过: 备注表.xlsx/Sheet: 找不到表头 来源文件 工作表 匹配方式 订单号 客户名称 金额 9月订单.xlsx Sheet 内容包含 A002 北京喵喵科技 200 导出表.xlsx Sheet 内容包含 B001 喵喵科技上海 (补空列)
注意「导出表」那行——它的表头(编号/公司)和第一个命中的文件(订单号/客户名称/金额)列数不一样,脚本自动补了空列对齐,Excel 里打开不会串列。连跑三次输出文件 md5 完全一致。
python multi_search.py 文件夹路径 关键字 输出结果.xlsx
搜索不区分大小写;表头行自动识别(表头前面有几行说明文字、空行也能跳过);打不开的文件和找不到表头的 sheet 不会中断,会列在结尾的「跳过」清单里。
# -*- coding: utf-8 -*-
"""多文件关键字查找汇总:在一个文件夹的所有 xlsx 里搜关键字,命中行汇总到一个结果表。"""
import os, sys, glob
from openpyxl import load_workbook, Workbook
import datetime as dt
folder, keyword, dst = sys.argv[1], sys.argv[2], sys.argv[3]
def cell_text(v):
if v is None:
return ""
if isinstance(v, dt.datetime):
return v.strftime("%Y-%m-%d %H:%M:%S")
return str(v).strip()
# 找出有表头的第一行(有的表上面有几行说明文字)
def find_header(ws):
for r in range(1, min(ws.max_row, 20) + 1):
vals = [cell_text(c.value) for c in ws[r]]
if sum(1 for v in vals if v) >= 1 and any(v for v in vals):
return r, vals
return None, []
out = Workbook()
now = dt.datetime(2026, 1, 1)
out.properties.created = now
out.properties.modified = now
s1 = out.active
s1.title = "汇总"
files = sorted(glob.glob(os.path.join(folder, "*.xlsx")))
total_hit = 0
skipped = []
for fp in files:
fname = os.path.basename(fp)
try:
wb = load_workbook(fp, data_only=True) # data_only: 公式表拿计算值而不是公式串
except Exception as e:
skipped.append(f"{fname}: 打不开({e})")
continue
for wsname in wb.sheetnames:
ws = wb[wsname]
hr, header = find_header(ws)
if hr is None:
skipped.append(f"{fname}/{wsname}: 找不到表头")
continue
loc = [i for i, h in enumerate(header) if keyword.lower() in h.lower()]
col_match = len(loc) > 0
hits = []
for row in ws.iter_rows(min_row=hr + 1, values_only=True):
joined = " ".join(cell_text(v) for v in row)
if keyword.lower() in joined.lower():
hits.append([cell_text(v) for v in row])
if hits or col_match:
if total_hit == 0:
s1.append(["来源文件", "工作表", "匹配方式"] + (header if header else ["(无表头)"]))
for h in hits:
s1.append([fname, wsname, "内容包含"] + h + [""] * (len(header) - len(h)))
if not hits and col_match:
s1.append([fname, wsname, "仅表头含关键字"] + [""] * len(header))
total_hit += len(hits)
out.save(dst)
print(f"扫描 {len(files)} 个文件,命中 {total_hit} 行,结果已写入 {dst}")
for s in skipped:
print("跳过:", s)
data_only=True,公式表搜到的是公式串不是值。测试里那张「备注表」单元格里写的是 =UPPER("miao tech order")——不传 data_only 时读到的字符串就是公式本身,你搜「miao tech」永远搜不到计算结果。加上 load_workbook(fp, data_only=True) 才拿缓存计算值(注意:如果文件从没被 Excel 打开计算过,缓存值可能是空,这是 openpyxl 的已知边界)。[""] * (len(header) - len(h)) 强制对齐。🎈 没时间自己跑脚本?喵窝资源铺提供代跑服务:把文件发给我们,AI 执行 + 人工核验后把结果文件发回。查看全部工具与服务 →(虚拟物品,交付后不退款,下单前请确认需求)