ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Python 读取 10 万行 Excel 实测:各库耗时与内存对比

Python 读取 10 万行 Excel 实测:各库耗时与内存对比 Excel 数据读取看起来是入门操作但一旦数据量到 10 万行很多问题就会集中冒出来文件打开慢、单元格逐行读取慢、内存占用高、日期和字符串类型错乱。这次我们不空谈“哪个库最快”而是围绕“用 Python 常见库读取 10 万行 Excel 到底要多久”这个具体问题把测试目标、参与库、环境变量、读取脚本和判断方法完整铺开。你会看到 openpyxl、xlrd、pandas 等常见方案的能力边界也会理解为什么同一个文件在不同读取方式下得到的时间可能完全不是一个量级。先把结论形态说清楚这种评测最怕只给一个秒数。同样的 10 万行数据列数变多、字符串变长、日期类型增加、文件是否带样式、是否走系统缓存都会显著改变结果。所以这篇文章不只是给答案更重要的意义在于提供一套可重复执行的测试流程包括 10 万行测试数据怎么生成、不同库分别用什么代码读取、怎么统计多次运行的中位数、怎么看内存峰值最后再补上批量读取多个 Excel 文件的工程写法。你完全可以把这份脚本搬到自己机器上跑出一组自己环境的结果再决定生产环境用哪种方案。适合谁看日常用 Python 处理 Excel 报表、写数据导入脚本、要把 Excel 解析封装成后台任务的开发者都适用。这个主题不涉及 GPU也不需要深度学习环境普通 CPU 机器加常见 Python 包就能跑。真正需要注意的点是文件格式、读取引擎、是否流式、是否构建 DataFrame这四件事通常比库本身的“网上跑分”更影响结论。1. 核心能力速览评测项说明评测目标用 Python 常见库读取 10 万行 Excel比较耗时与稳定度数据规模10 万行 X 5 列包含数值、字符串、日期等常见类型文件格式以 .xlsx 为主.xls、.csv 作为对比说明参与库pandas、openpyxl、xlrd可选 xlsxwriter、pyxlsb、polars 等运行环境普通 CPU 电脑即可不依赖 GPU内存建议按本机实际情况观察核心操作生成测试文件、单文件计时、流式读取、批量读取、内存监控批量任务支持可通过目录遍历批量处理是本地脚本形式不是网络 API接口 API无现成 Web API可按需用 FastAPI 等自行封装适用读者Python 数据处理、报表导入导出、办公自动化开发人员2. 为什么选择“10 万行”作为测试规模Excel 单表行数上限约是 104 万行10 万行只是它的十分之一左右。这个规模在办公场景里很典型一份订单明细、客户名单、设备台账、日志导出很容易积累到 10 万行。用 Excel 直接打开这种文件拖动和筛选已经开始有卡顿感但还没有到完全打不开的程度。对于 Python 脚本来说10 万行也刚好能区分“纯解析 XM L”和“解析后构建 DataFrame”的不同代价。另一个原因是文件体积和测试速度的平衡。如果只测几千行绝大多数库的耗时都在毫秒级差距可能被随机波动淹没如果直接上 100 万行openpyxl 默认模式读取会非常吃力可能会因为长时间占用内存而让脚本中途崩掉。10 万行更容易跑完整个对比矩阵也更贴近真实业务中“这份表有点大但还得用 Python 处理”的状态。还要提醒一点10 万行并不是一个固定不变的测试条件。同一批数据保存成不同格式时内部结构差异很大。.xlsx 本质是 zip 压缩的 XML 文件除了单元格数据还有可能包含共享字符串、样式、主题、图表等附加内容。而 .xls 是老的二进制格式甚至还有单表行数 65536 的上限10 万行本身就无法放进一个 .xls 工作表。因此做这类评测前最好先明确你测的到底是哪个扩展名、哪些列、什么类型的数据。3. Python 常见 Excel 读取库选型库主要支持格式定位注意事项pandas.xlsx、.xls、.xlsb 等数据分析、表结构统一底层依赖 openpyxl、xlrd 等引擎openpyxl.xlsx、.xlsm 等读写并保留样式支持只读流式普通模式不是向量化读取速度偏慢xlrd.xls老格式读取新版不再支持 .xlsx只适合老 .xlsxlsxwriter.xlsx写入 Excel不是读取库写入性能有参考价值pyxlsb.xlsb读取二进制 Excel适合 .xlsb 格式但不是默认首选标准库 csv.csv纯文本对照不是 Excel但常用于“Excel 转出后处理”polars / python-calamine多种格式高性能读取是否可用取决于安装版本和引擎支持从材料看这轮评测至少要覆盖 pandas 和 openpyxl。pandas 的read_excel接口统一适合后续数据分析openpyxl 能直接操作工作簿和单元格更适合需要保留格式或逐行处理的场景。xlrd 则用来验证老 .xls 文件不能拿它去强行读 .xlsx否则会直接报“Excel xlsx file; not supported”。在这类选型里不要只看库的名字还要看它背后用的解析引擎。pandas 读 .xlsx 时默认会调 openpyxl如果你的 pandas 版本支持calamine之类的加速引擎那读取耗时可能是另一套表现。最稳妥的方式是固定库版本和引擎参数再做横向对比。4. 环境准备与测试数据构造4.1 安装依赖先创建一个干净的虚拟环境再安装需要对比的库。项目测试过程中环境隔离能避免系统 Python 目录里的旧包影响结果。python -m venv excel_bench_env source excel_bench_env/bin/activate pip install --upgrade pip pip install pandas openpyxl xlrd xlsxwriter pyxlsb如果你的 pandas 版本支持高性能读取引擎也可以单独安装加速引擎pip install python-calamine polars安装完成后最好确认一下版本。版本不同read_excel的默认行为可能会有细微差别。import pandas as pd import openpyxl import xlrd import sys print(Python:, sys.version) print(pandas:, pd.__version__) print(openpyxl:, openpyxl.__version__) print(xlrd:, xlrd.__version__)4.2 生成固定种子数据做耗时测试时测试数据必须固定。如果每跑一次都重新随机生成文件内容不一样单元格长度和字符串分布可能每次不同得到的耗时就没有可比性。下面使用固定随机种子生成 10 万行 X 5 列数据包含整数、浮点数、分类字符串、长字符串、时间戳五种常见类型。import numpy as np import pandas as pd from pathlib import Path rows 100_000 rng np.random.default_rng(42) df pd.DataFrame({ id: np.arange(rows), amount: rng.uniform(0, 100000, rows), category: rng.choice([A, B, C, D], rows), note: [frow-{i}-python-excel-benchmark for i in range(rows)], created_at: pd.date_range(2024-01-01, periodsrows, freqmin), }) xlsx_path Path(data_100k.xlsx) df.to_excel(xlsx_path, indexFalse) print(file size MB:, round(xlsx_path.stat().st_size / 1024 / 1024, 2))这里选择 5 列而不是 1 列目的是让测试更接近真实表格。实际业务中10 万行往往不止一列而且字符串列会显著影响文件大小和解析耗时。如果你想测更多列可以直接在 DataFrame 里增加列字段保存后重新生成文件。要注意如果你非要测 .xls 格式请先记住 .xls 工作表的行数上限是 6553610 万行数据放不下。更合理的做法是只对 .xlsx、.xlsb 这类格式做 10 万行对比或者把 .xls 场景单独降级到 5 万行再说明。5. 读取耗时测试流程设计5.1 统一计时口径耗时测试最怕口径不一致。同样是“读取 Excel”可以拆成多种不同的完成标志只打开工作簿不读任何单元格。把所有单元格遍历一遍但不保存成表。把单元格内容转成 Python 对象列表。将内容直接加载成 pandas DataFrame。在 DataFrame 基础上做类型推断和日期解析。这几种口径的耗时差异可能很大。所以测试前要先想清楚业务需要什么。如果后面只是做数据清洗就要测到 DataFrame 加载完成如果只是想快速检查表里有没有某些值用 openpyxl 的只读流式模式可能更合适但它在“能直接做数据分析”这个能力上是欠缺的。下面给出一个计时函数统一使用time.perf_counter并重复多次后取中位数。import gc import time import statistics def benchmark(func, repeat3): times [] for _ in range(repeat): gc.collect() start time.perf_counter() result func() delta time.perf_counter() - start times.append(delta) del result return statistics.median(times)使用中位数而不是平均值可以减少系统调度、垃圾回收和 CPU 降频带来的偶发波动。第一次运行和第二次运行往往会因为操作系统文件缓存而出现较大差异所以不要只跑一次就下结论。5.2 pandas 读取测试pandas 是最常见的 Excel 读取方式代码最简单后续处理能力也最强。file_path data_100k.xlsx def read_pandas_openpyxl(): return pd.read_excel(file_path, engineopenpyxl, sheet_name0) median_time benchmark(read_pandas_openpyxl) print(fpandas openpyxl median: {median_time:.3f} s)如果你的环境安装了 calamine 引擎且 pandas 版本支持可以把引擎改成calamine再测一次。要注意的是引擎不同最终得到的 DataFrame 类型不一定完全一致需要使用同样的校验条件来验证。def read_pandas_calamine(): return pd.read_excel(file_path, enginecalamine, sheet_name0)如果你要读的其实是 .xls 老文件pandas 的引擎应该切换成xlrd。这也能解释为什么不要只记一句话“pandas 慢”或者说“openpyxl 快”因为你没有说明文件类型和引擎参数。5.3 openpyxl 只读流式读取测试openpyxl 的普通模式会把工作表和样式都加载到内存遇到 10 万行大表会明显变慢。它还有一个read_onlyTrue模式可以流式读取单元格不会一次性把所有行都加载成对象。下面测试的是遍历全部单元格并统计行数不转 DataFrame。from openpyxl import load_workbook def read_openpyxl_stream(): wb load_workbook(file_path, read_onlyTrue, data_onlyTrue) ws wb.active count 0 for row in ws.iter_rows(values_onlyTrue): if count 0: # 第一行是表头 count 1 continue count 1 wb.close() return count median_stream benchmark(read_openpyxl_stream) print(fopenpyxl read_only stream median: {median_stream:.3f} s)这段代码里用data_onlyTrue是为了读取公式计算后的缓存值而不是公式本身。如果在实际业务中你需要的恰恰是公式文本那就要把data_only设成False这也可能导致读取结果完全不同。5.4 校验读取结果耗时比较不能只看快慢还要确认读出来的结果是对的。否则最快的方式可能就是“读错数据”。校验点主要有三个行数是否等于 10 万、列名是否匹配、非空数据量是否正确。df_check pd.read_excel(file_path, engineopenpyxl, sheet_name0) print(shape:, df_check.shape) assert df_check.shape[0] 100_000 assert list(df_check.columns) [id, amount, category, note, created_at] print(amount sum:, df_check[amount].sum())如果你的测试文件是 CSV 读入的再写入 Excel日期可能会被转成字符串读取时就要额外指定parse_dates。这属于数据污染的常见问题先被校验逻辑拦下来后面才不会把错误结果当成性能结论。6. 影响读取耗时的关键变量同样是“10 万行”为什么大家在社区里给出的体验差别很大根本原因在于行数不是唯一变量真正决定耗时的是解析器需要处理的“单元格数量”和“内容复杂度”。第一单元格数量。10 万行 X 5 列是 50 万个单元格10 万行 X 20 列就是 200 万个单元格。单元格数量翻倍耗时通常不会只增加一点点。所以报告耗时的时候最好同时写明列数。第二字符串长度和共享字符串机制。xlsx 文本内容通常存在共享字符串表里字符串越多越长解析和匹配成本就越高。如果一整列都是“row-0-xxx”这种带前缀的文本测试结果会更接近真实但偏重的场景。第三文件压缩率和底层存储。xlsx 是 zip 压缩包读取时要解压 XML。如果文件压缩率高磁盘读取量更少但 CPU 解压成本更高如果文件放在机械硬盘、网络盘或 U 盘上IO 等待会成为主要瓶颈。这提醒我们测速前应确认文件在本机本地磁盘避免网络盘干扰。第四是否读取样式。openpyxl 普通模式会接触工作簿的更多结构如果只要数据设置read_onlyTrue能明显减少样式和绘图对象带来的额外处理。这里并不是说样式信息没有用而是要看你的业务到底需不需要。第五日期和类型推断。pandas 读取后会把日期列解析成时间类型需要额外判断单元格格式openpyxl 流式遍历时拿到的则可能是一个 datetime 对象或字符串这取决于文件内部写法。类型推断看起来很轻但在几十万单元格上累积起来并不小。第六系统文件缓存。第一次读取文件后操作系统会把文件放进缓存第二次读取可能大幅提速。所以正确做法是轮流跑多个库或安排多次重复取中位数。不要简单地把第二次 fast 归因于某个库更强。第七GC 和内存抖动。大 DataFrame 读取和删除过程中Python 的垃圾回收会产生随机停顿。脚本里每次计时前执行gc.collect()可以降低前一任务在内存中残留造成的干扰。第八文件是否包含公式、条件格式、数据验证、合并单元格等复杂内容。公式要缓存的值条件格式和合并单元格本身也会增加解析负担。这类文件更适合用 openpyxl 做结构化处理而不是交给极简的 csv 思路。7. 批量读取多个 Excel 与本地模块化调用这个场景没有对外提供“网络接口 API”但很适合封装成可供其他脚本调用的本地模块。实际工作中经常需要一次性处理多个 Excel 文件比如某个目录下每天都有新的报表需要读取。下面给出一个批量读取的思路逐个文件记录读取耗时、行数和形状。import time from pathlib import Path import pandas as pd def read_multi_excel(input_dir: str, engine: str openpyxl): results [] for fp in sorted(Path(input_dir).glob(*.xlsx)): start time.perf_counter() try: df pd.read_excel(fp, engineengine, sheet_name0) results.append({ file: fp.name, ok: True, shape: df.shape, seconds: round(time.perf_counter() - start, 4), }) except Exception as exc: results.append({ file: fp.name, ok: False, error: str(exc), seconds: round(time.perf_counter() - start, 4), }) return results if __name__ __main__: for item in read_multi_excel(excel_files): print(item)批量读取时要考虑失败重试。很多临时性失败并不是逻辑错误而是文件正在被其他程序占用或者网络盘临时不可用。可以先把失败文件记录下来等待一段时间后重试而不是让整个任务中断。import time def read_with_retry(path: Path, retries: int 2, wait_seconds: float 1.0): last_error None for attempt in range(1 retries): try: return pd.read_excel(path, engineopenpyxl, sheet_name0) except Exception as exc: last_error exc if attempt retries: time.sleep(wait_seconds) raise last_error如果只是想给外部程序提供 API可以用 FastAPI 包一层/read-excel接口。但接口传输的还是文件路径或文件内容真正的性能瓶颈还是读取引擎本身。不要指望加了 HTTP 层之后一个原本很慢的 openpyxl 普通读取就会变成毫秒级。8. 资源占用与性能观察方法这类测试除了计时还要关注内存变化。不同库的内存模型差异比很多人想象的更大。pandas 会把数据读入结构化数组内存开销高但后续计算方便openpyxl 的只读流式模式不会一次载入全部单元格但如果你把遍历出来的数据都保存在 list 里最终内存一样会上升。可以用psutil记录当前进程的最大内存占用把资源数据和耗时一起输出。import os import psutil process psutil.Process(os.getpid()) def memory_usage_mb() - float: return process.memory_info().rss / 1024 / 1024在测试脚本里读取前调用一次读取后再调用一次两者的差值就是这次读取带来的新增内存。注意Python 进程本身导入 pandas 和 openpyxl 会占用一定基础内存这部分要在对比时说明避免误判为“读取 10 万行 Excel 就需要这么多内存”。观察性能时重点不是单看峰值。高内存占用配合快速执行在内存充足的机器上是可以接受的但如果你的目的是在 8GB 内存的服务器上批量跑任务那就必须优先选择流式方案并在每个文件处理完后尽快释放对象。需要降低内存占用可以参考以下思路用usecols只读需要的列不要全表加载。如果只是做字段提取先考虑 openpyxl 只读流式。pandas 读取大文件后及时删除临时 DataFrame并调用gc.collect()。数据量持续增长时不要继续坚持 xlsx 作为中间格式转存为 Parquet 或 SQLite 再做高频处理。批量任务中分文件记录日志方便定位是哪个文件导致的资源尖峰。9. 常见问题与排查方法问题现象可能原因排查方式解决方案运行代码提示缺少 openpyxl虚拟环境没有安装依赖pip list查看已安装包执行pip install openpyxl用 xlrd 读 .xlsx 报错xlrd 新版只支持 .xls查看报错提示是 xlsx not supported改用 openpyxl 或 pandas 引擎文件打开后报 BadZipFile文件损坏或并非真正 xlsx用压缩工具打开文件确认结构重新导出或使用原文件备份读取后日期列变成字符串或数字Excel 日期内部存储和解析规则不同打印 DataFrame 列 dtype用parse_dates或读取后pd.to_datetime读取结果出现 NaN单元格为空或 pandas 类型推断导致检查原始表格空值分布用keep_default_na等参数控制内存占用过高pandas 全量加载所有列观察任务管理器/psutil只读需要的列或改用流式读取连续读取两次耗时差异很大系统文件缓存影响重置缓存或多次取中位数多次运行按中位数比较10 万行 .xls 保存失败.xls 行数上限是 65536确认文件格式限制改用 .xlsx 或拆分文件读取出来的公式值为空没有读取公式缓存值检查data_only参数需要缓存值设True需要公式文本设False批量任务处理到一半中断某个文件损坏或格式特殊给循环加 try except记录失败文件并设置重试常见问题排查的顺序也很重要先看报错是来自语法、依赖还是文件本身先读小文件验证脚本再上 10 万行先固定文件路径和版本再谈优化。很多时候问题不是某个库不行而是环境、文件格式和读取参数之间不匹配。10. 读取方案选型与最佳实践不同场景下的推荐方案并不相同。如果你的需求是几十行到几千行的轻量读取pandas 的read_excel最方便代码最统一。如果是 10 万行左右、需要清洗和分析pandas 配合 openpyxl 引擎已经足够关键是要避免使用 openpyxl 逐单元格手动拼 DataFrame那样的写法会放大性能差距。如果是超大 xlsx 文件而且只想按行扫一遍做简单过滤openpyxl 的read_onlyTrue流式模式会更合适但你需要接受一个现实它返回的是单元格生成器而不是可以直接做列聚合的 DataFrame。如果你需要同时保留高效读取和 DataFrame 分析能力常见的工程化选择是先把 xlsx 转成 CSV 或 Parquet再进入分析流程。这里的“转格式”看起来多了一步在重复读取多次时往往反而更快。还有几个工程化建议值得养成习惯。第一第一次使用新库时要先读小文件验证功能确认行数、列数和类型正确后再测大文件。第二保留一套最小可运行配置把文件生成、计时、校验分开不要在一个脚本里混入可视化、打印、网络请求等无关操作。第三模型文件、输入数据、输出结果分目录管理Excel 类临时文件和正式数据不要混在一个目录里。第四批量任务必须加日志至少记录每个文件的成功状态、处理耗时和异常信息。第五接口服务如果对外开放要限制访问范围防止别人通过接口上传超大文件把进程内存打满。在合法合规方面处理 Excel 数据时要考虑数据来源和授权边界。如果表格里包含个人姓名、电话、地址、人脸、声音等敏感信息读取和存储都要遵循最小必要原则不要为了做性能测试而随意使用他人真实数据。测试阶段尽量用自己生成的数据例如本文里的随机订单数据这是最稳妥的方式。关于“前端和后端如何配合”其实不在这次评测范围里但有一点值得强调如果把 Excel 解析放到后台服务建议对上传文件做格式白名单、大小限制和并发数限制。否则即使某个读取库本身很快恶意上传或异常文件也会拖垮服务。11. 总结与下一步这次评测的核心不是追求一个“谁最快”的单一答案而是搞清楚 Python 读取 Excel 时哪些条件会影响最终耗时。最容易踩的坑有三个第一只比库名不比引擎开放 pyxl 和 pandas 在同一个文件上本来就不是同一层操作第二只比耗时不管内存高内存方案在本地能跑上线后很可能 OOM第三只看读取不校验结果一旦类型解析错误性能结论就失去了意义。如果你想在自己机器上复现建议先跑通数据生成脚本再执行 pandas 和 openpyxl 两组计时最后用 psutil 观察内存。拿到结果后可以进一步扩展的方向包括不同列数下的耗时变化、字符串占比对解析速度的影响、读取后转 Parquet 再分析的性能、以及用 FastAPI 封装本地读取接口的并发能力。这篇文章里的代码可以直接复制替换成你的实际文件路径就能得到一份属于自己环境的评测结论。建议先收藏备用之后遇到“Python 读大 Excel 慢”的问题时可以对着这些变量逐项排查。
返回列表