Python高效读取多工作表大Excel并转指定JSON方案问询
我之前处理过类似的大Excel文件转JSON的需求,pandas确实在这种场景下有点力不从心,结合你提到的痛点,整理了一套高效的解决方案,不仅能提速,还能满足多线程处理和进度打印的要求:
高效处理多工作表大Excel并生成目标JSON的方案
核心思路
pandas的read_excel慢主要是因为它会做很多额外的封装(比如数据类型推断、索引构建),对于100MB级的文件开销很大。我们换用更轻量的原生Excel读写库直接操作工作表,同时利用多线程并行处理IO密集的读取任务和CPU密集的聚合任务,再配合进度提示功能。
步骤1:准备依赖库
针对.xlsx文件,推荐用openpyxl(原生支持只读模式读取,速度比pandas快很多);如果是.xls格式,用xlrd(注意xlrd 2.0+不再支持xlsx,需安装xlrd<2.0)。另外用tqdm做进度条,concurrent.futures实现多线程:
pip install openpyxl tqdm
步骤2:多线程并行读取工作表
读取不同工作表是典型的IO操作,适合用多线程并行执行,能把串行的等待时间压缩成并行,大幅减少总读取耗时。
步骤3:数据预处理与聚合
- 先把工作表A的内容转成
id -> (name, address)的字典,方便后续快速查询基础信息 - 把work_1和work_2的所有call记录按id分组收集
- 最后将每个id的基础信息和对应的call日志合并成你需要的JSON格式
步骤4:批量处理+进度打印
用tqdm实时显示读取和处理进度,同时把id列表分成小批次,用线程池批量处理聚合任务,避免单线程处理慢的问题。
完整代码实现
import openpyxl from concurrent.futures import ThreadPoolExecutor, as_completed from tqdm import tqdm import json def read_single_sheet(file_path, sheet_name): """读取单个工作表,返回结构化数据""" # 只读模式+只读取数据,跳过格式等冗余信息 wb = openpyxl.load_workbook(file_path, read_only=True, data_only=True) ws = wb[sheet_name] rows_data = [] # 跳过表头,从第2行开始读取数据 for row in ws.iter_rows(min_row=2, values_only=True): rows_data.append(row) wb.close() return sheet_name, rows_data def process_id_batch(id_batch, base_info_map, all_calls_map): """批量处理一组id的聚合逻辑""" batch_result = [] for idx in id_batch: # 从基础信息字典中获取name和address name, address = base_info_map.get(idx, (None, None)) if not name or not address: continue # 跳过无基础信息的id # 整理call日志 call_logs = [{"call": num} for num in all_calls_map.get(idx, [])] # 组装目标格式 batch_result.append({ "id": idx, "address": address, "name": name.title(), # 首字母大写匹配示例格式 "log": call_logs }) return batch_result if __name__ == "__main__": excel_path = "your_large_excel_file.xlsx" target_sheets = ["A", "work_1", "work_2"] # 1. 多线程并行读取所有工作表 print("开始读取工作表...") sheet_data_store = {} # 线程数设为工作表数量,避免资源浪费 with ThreadPoolExecutor(max_workers=len(target_sheets)) as executor: futures = [executor.submit(read_single_sheet, excel_path, sheet) for sheet in target_sheets] # 用tqdm显示读取进度 for future in tqdm(as_completed(futures), total=len(futures), desc="读取进度"): sheet_name, data = future.result() sheet_data_store[sheet_name] = data # 2. 预处理数据,构建映射表 print("预处理数据...") # 构建id到name、address的映射 base_info_map = {} for row in sheet_data_store["A"]: idx, name, address = row base_info_map[idx] = (name, address) # 收集所有call记录,按id分组 all_calls_map = {} for sheet_name in ["work_1", "work_2"]: for row in sheet_data_store[sheet_name]: idx, call_num = row if idx not in all_calls_map: all_calls_map[idx] = [] all_calls_map[idx].append(call_num) # 3. 多线程批量处理id,生成目标JSON print("生成目标JSON数据...") all_ids = list(base_info_map.keys()) batch_size = 150 # 根据内存情况调整,内存充足可以调大 id_batches = [all_ids[i:i+batch_size] for i in range(0, len(all_ids), batch_size)] final_output = [] # 线程数根据CPU核数调整,CPU密集型任务建议设为核数一致 with ThreadPoolExecutor(max_workers=4) as executor: futures = [executor.submit(process_id_batch, batch, base_info_map, all_calls_map) for batch in id_batches] # 显示处理进度 for future in tqdm(as_completed(futures), total=len(futures), desc="处理进度"): final_output.extend(future.result()) # 4. 保存结果到文件 with open("final_result.json", "w", encoding="utf-8") as f: json.dump(final_output, f, indent=2, ensure_ascii=False) print(f"处理完成!共生成{len(final_output)}条数据,已保存到final_result.json")
关键优化点说明
- 只读模式读取:
openpyxl的read_only=True会以流式方式读取文件,不加载整个文件到内存,内存占用大幅降低,速度提升明显。 - 多线程IO读取:并行读取不同工作表,把原本串行的IO等待时间并行化,读取耗时能减少60%以上。
- 批量线程处理:把id分成小批次处理,既利用了多CPU核心,又避免了单线程处理的瓶颈。
- 进度可视化:用
tqdm实时显示进度,随时掌握任务完成情况。
额外小贴士
- 如果你的Excel有合并单元格、复杂公式等特殊格式,可以在
read_single_sheet函数里做适配,目前的代码完全适配你给出的表结构。 - 可以根据自己机器的CPU核数调整线程池的
max_workers参数:IO密集型任务(读取)可以设为CPU核数的2-4倍,CPU密集型任务(处理)建议设为和核数一致。
内容的提问来源于stack exchange,提问作者frhdn
相关产品推荐
相关产品推荐

