在日常办公中,经常需要从多个Excel工作表中整理出一份去重后的清单。例如一个工作簿包含1月到12月的销售表,每张表都有“产品名称”列,我们希望得到所有出现过的产品名称,而不关心具体销量。手动操作需要逐个复制、粘贴、删除重复项,效率低且容易出错。本文介绍用Python和xlwings库批量提取一个工作簿中所有工作表的唯一值,并输出为纵向排列的新工作簿。
适用场景:多个工作表结构类似且包含同一目标列,比如产品名称、员工姓名、客户名称、设备型号等。如果表头名称不统一,需要预先做字段映射或修改代码处理。
核心原理是遍历所有工作表,将目标列的每个值收集到一个列表中,然后用set()去重,最后将结果纵向写入Excel。set天然不含重复元素,但会打乱原始顺序;如果需要保留首次出现顺序,可以用dict.fromkeys()替代。去重后可以借助sorted()排序。
下面给出完整的批量提取唯一值代码。先修改file_path、target_col和out_file三个参数,分别指定源文件路径、目标列名和输出文件路径。
- import xlwings as xw
- file_path = r"e:\file\销售数据.xlsx"
- target_col = "产品名称"
- out_file = r"e:\file\产品名称清单.xlsx"
- app = xw.App(visible=False, add_book=False)
- try:
- wb = app.books.open(file_path)
- all_values = []
- for sht in wb.sheets:
- table = sht.range("A1").current_region.value
- if not table or len(table) < 2:
- print(f"跳过:{sht.name},没有可处理的数据")
- continue
- header = table[0]
- rows = table[1:]
- if target_col not in header:
- print(f"跳过:{sht.name},未找到列:{target_col}")
- continue
- col_idx = header.index(target_col)
- count = 0
- for row in rows:
- if col_idx >= len(row):
- continue
- value = row[col_idx]
- if value is None or str(value).strip() == "":
- continue
- all_values.append(str(value).strip())
- count += 1
- print(f"已读取:{sht.name},提取 {count} 个值")
- wb.close()
- unique_values = sorted(list(set(all_values)))
- unique_values.insert(0, target_col)
- out_wb = app.books.add()
- out_sht = out_wb.sheets[0]
- out_sht.name = "唯一值清单"
- out_sht.range("A1").options(transpose=True).value = unique_values
- out_sht.autofit()
- out_wb.save(out_file)
- out_wb.close()
- print(f"清单生成完成:{out_file}")
- print(f"唯一值数量:{len(unique_values) - 1}")
- finally:
- app.quit()
复制代码
代码中关键处理:将每个值转为字符串并strip()去除前后空格,避免“背包”和“背包 ”被视为不同值。如果目标列包含数字编号(如001和1),直接转字符串可能产生歧义,需根据实际数据格式调整。out_sht.range("A1").options(transpose=True).value = unique_values实现纵向写入,符合清单阅读习惯。
运行后不要只看成功提示,建议从三个方面验证:检查控制台输出是否每张表都被读取;对比唯一值数量是否合理;打开输出文件确认第一行有表头且数据纵向排列。
举一反三:如果需要统计每个产品累计销量,可将set升级为dict。下面是按“产品名称”累计“销量”的示例:
- import xlwings as xw
- file_path = r"e:\file\销售数据.xlsx"
- col_product = "产品名称"
- col_qty = "销量"
- out_file = r"e:\file\产品销量汇总.xlsx"
- app = xw.App(visible=False, add_book=False)
- try:
- wb = app.books.open(file_path)
- stat = {}
- for sht in wb.sheets:
- table = sht.range("A1").current_region.value
- if not table or len(table) < 2:
- continue
- header = table[0]
- rows = table[1:]
- if col_product not in header or col_qty not in header:
- continue
- idx_p = header.index(col_product)
- idx_q = header.index(col_qty)
- for row in rows:
- if idx_p >= len(row) or idx_q >= len(row):
- continue
- product = row[idx_p]
- qty = row[idx_q]
- if product is None or str(product).strip() == "":
- continue
- product = str(product).strip()
- if qty is None or qty == "":
- qty = 0
- qty = float(qty)
- stat[product] = stat.get(product, 0) + qty
- wb.close()
- out_wb = app.books.add()
- out_sht = out_wb.sheets[0]
- out_sht.name = "产品销量汇总"
- out_sht.range("A1").value = [["产品名称", "累计销量"]]
- result = sorted(stat.items(), key=lambda x: x[0])
- out_sht.range("A2").value = result
- out_sht.autofit()
- out_wb.save(out_file)
- out_wb.close()
- print(f"统计完成:{out_file}")
- print(f"产品数量:{len(stat)}")
- finally:
- app.quit()
复制代码
stat.get(product,0)+qty是字典累加常用写法。注意销量字段必须能转成数字,否则float()会报错,可提前清洗或异常处理。
常见问题:表头不一致导致部分sheet被跳过;空格导致去重失败;set去重后顺序变化;隐藏工作表会被遍历。正式运行前建议备份文件,先打印工作表名称确认范围。
总结:唯一值提取的本质是“遍历收集→去重→输出”,可以沉淀为通用工具。将源路径、目标列、输出路径参数化后,即可复用于产品、客户、部门、城市等多种清单生成。记住三个要点:先明确目标列、先清洗再去重、输出后必须验证。自动化不是跑完就结束,而是交付可复查的结果。 |