Python利用pandas与openpyxl读取10万行Excel的耗时实测
作者:菩提风
在处理 Excel 表格时,经常会冒出一个灵魂拷问:用 Python 读取大量数据,到底有多快?特别是面对那种动不动就几万行、十万行甚至更多的表格,不同库的耗时差异可能非常大。这篇文章直接围绕“Python 常见库读取 10 万行 Excel 耗时”这个主题做一轮实测记录,帮你扫掉选型盲区。
先说结论:如果你只是偶尔读一次几万行的 Excel,很多库的耗时差异感知不明显;但如果你要批量处理几十个文件,或者经常做数据集成、报表导入,读取耗时就直接影响任务吞吐。对于 10 万行数据量级,pandas 这类数据分析库往往更稳,但 xlsx 格式本身的解析速度也会拖后腿;而一些偏底层的库在某些场景下反而更快,但使用门槛更高。不同库之间真正拉开的差距,主要体现在文件格式、单次读取方式、是否带样式解析、以及后续数据处理需求上。
这篇文章适合这几类人:正在做 Python Excel 处理技术选型的开发者、需要批量导入报表的数据分析同学、以及想搞明白为什么 openpyxl 越跑越慢的初学者。下面按实际测试顺序拆一遍,不吹性能,只讲怎么测、怎么判、怎么避坑。
1. 先确认测试对象和痛点:10 万行到底卡在哪
1.1 为什么单看读取速度会失真
在做库对比之前,先要明确一个问题:读取 Excel 的耗时,并不只取决于“文件里有多少行”,而是由下面这些因素一起决定:
- 文件格式:xlsx、xls、xlsm、xlsb、csv,底层解析逻辑完全不同。
- 单元格内容:是纯数字、短文本,还是大量长文本、公式、超链接、批注。
- 单元格数量:10 万行乘以 10 列就是 100 万个单元格,这跟 10 万行乘以 3 列完全不是同一个量级。
- 是否保留样式:部分库默认不关心样式,部分库即使不主动读取样式,也会在加载时付出额外解析成本。
- 是否读取全部数据:有时只需要某一列,但很多库没法做到真正的列裁剪。
很多所谓“读 10 万行慢”的报错和体验,本质不是行数问题,而是单元格总量和解析方式问题。建议测试时先统一这些口径,不然不同库的对比结果很难说明问题。
1.2 常见 Python Excel 库的适用场景
在正式做耗时对比之前,先把几个常见库按适用场景理一遍。这样后面的测试结果放在具体场景里才有意义。
- pandas:数据分析首选。它本身不是专门解析 Excel 的库,而是通过 openpyxl、xlrd 等底层库作为引擎。优点是 API 友好,读进来直接是 DataFrame,后续统计和处理非常方便。
- openpyxl:专门处理 xlsx 的库,能读写、能处理样式、公式、图表。缺点就是单纯读大文件时,速度不算出色。
- xlrd:老牌库,2.0 之后只支持 .xls,不再支持 .xlsx。如果你的历史文件是 .xls,可以用它。
- xlwt:写 .xls 用的,读取场景用不到。
- pyxlsb:用于读取 .xlsb 二进制格式,这种格式在部分企业报表系统里比较常见。
- csv 标准库:严格来说不是 Excel 文件,但很多导出数据实际上就是 CSV。处理超大表格时速度优势明显。
做对比测试前,先确认文件后缀和自己需要处理的格式。如果源文件是 xlsx,那测试范围重点可以放在 pandas 和 openpyxl 上;如果有 xls 文件,再考虑 xlrd。
2. 搭建测试环境:统一格式、统一数据口径
2.1 环境准备
实测不用特别高的服务器配置,普通办公电脑就能看趋势。建议先确认三个基础项:Python 版本、库版本、测试文件格式。
python --version pip list | findstr -i "pandas openpyxl xlrd xlsxwriter"
如果没有安装相关库,可以按需安装:
pip install pandas openpyxl xlrd xlsxwriter
这里有个容易忽略的点:pandas 读取不同格式时,需要额外安装对应的引擎依赖。比如读取 xlsx 依赖 openpyxl,读取 xls 依赖 xlrd,读取 xlsb 依赖 pyxlsb。只装 pandas 就直接读 xlsx 也能跑通,但底层引擎版本不合适时会报错,或者性能不稳定。
2.2 生成 10 万行测试文件
为了测试统一,我一般会先自己生成一个 10 万行、10 列的 xlsx 文件,内容尽量模拟普通业务数据,比如订单 ID、日期、区域、商品名称、数量、单价、金额、备注等字段。
下面是用 openpyxl 生成测试文件的示例,这里只用于造数据,不属于耗时对比范围:
from openpyxl import Workbook
import random
import datetime
wb = Workbook()
ws = wb.active
ws.title = "sales_data"
headers = ["order_id", "order_date", "region", "product", "quantity", "unit_price", "amount", "remark"]
ws.append(headers)
product_list = ["键盘", "鼠标", "显示器", "U盘", "硬盘", "耳机", "摄像头", "音箱"]
region_list = ["华东", "华北", "华南", "西南", "西北"]
remark_list = ["正常单", "加急单", "退款单", "测试单", ""]
for i in range(1, 100001):
quantity = random.randint(1, 20)
unit_price = random.randint(20, 500)
order_date = datetime.date(2023, random.randint(1, 12), random.randint(1, 28)).strftime("%Y-%m-%d")
row = [
f"NO{i:08d}",
order_date,
random.choice(region_list),
random.choice(product_list),
quantity,
unit_price,
quantity * unit_price,
random.choice(remark_list),
]
ws.append(row)
wb.save("test_10w_rows.xlsx")
print("生成完成")
生成的文件大约包含 100 万个单元格,大小通常在 40MB 到 80MB 之间,具体取决于文本长度和压缩情况。
生成时要注意一点:如果以后要对比文件体积对读取速度的影响,尽量在生成后固定文件,不要反复生成字符串长度差异很大的版本。备注列如果全是超长文本,读取耗时和文件大小会明显上升。
2.3 测试脚本的统一口径
为了减少误差,每次测试都只调用一次读取方法,并记录从开始加载到数据可用的时间。用 time.perf_counter 是比较稳的做法。
import time
import pandas as pd
from openpyxl import load_workbook
start = time.perf_counter()
df = pd.read_excel("test_10w_rows.xlsx", sheet_name="sales_data", engine="openpyxl")
cost = time.perf_counter() - start
print(f"pandas + openpyxl 读取耗时: {cost:.3f} 秒")
print(f"读取结果: {df.shape}")
这里建议每次测试前都重新启动一个 Python 进程,不要在一个进程里连续测多次。原因是操作系统会缓存文件页面,连续第二次读取通常会比第一次快。要对比就统一跑“冷启动”场景,或者多次取均值。
3. 单文件读取实测:分格式测出真实差距
3.1 xlsx 格式下 openpyxl 与 pandas 的表现
在 xlsx 格式下,最常见的读取方式有两种:直接使用 openpyxl 的 load_workbook,以及通过 pandas 的 read_excel。
从实操体验上说,openpyxl 直接加载后得到的是 Workbook 对象,还要再通过 worksheet.iter_rows 或 worksheet.values 获取数据。pandas 则直接返回 DataFrame,后续处理更方便。
在 10 万行、10 列的数据测试中,我会分别跑以下两种方式:
# 方式一:openpyxl 直接读取为列表
from openpyxl import load_workbook
start = time.perf_counter()
wb = load_workbook("test_10w_rows.xlsx", read_only=True, data_only=True)
ws = wb["sales_data"]
rows = list(ws.iter_rows(values_only=True))
cost = time.perf_counter() - start
print(f"openpyxl read_only 读取耗时: {cost:.3f} 秒")
print(f"总行数: {len(rows)}")
wb.close()
# 方式二:pandas 读取
start = time.perf_counter()
df = pd.read_excel("test_10w_rows.xlsx", sheet_name="sales_data", engine="openpyxl")
cost = time.perf_counter() - start
print(f"pandas read_excel 读取耗时: {cost:.3f} 秒")
从个人实测经验看,openpyxl 必须开启 read_only=True 才适合读大文件。如果只是普通模式 load_workbook,耗时和内存占用都会明显上升。pandas 在底层使用 openpyxl 时,也是走文件流和行迭代,但封装后做了 DataFrame 转换,所以最终耗时和 openpyxl 直接读列表不一定谁快谁慢,要看机器配置和 xlsx 内部的 XML 结构。
需要注意一个反直觉的点:同样是 10 万行,xlsx 不是一种固定“格式”,它是一片 XML 压缩包。单元格数量相同,但字符串长、公式数量多、单元格格式多,都可能导致读取耗时差异达到 50% 以上。
3.2 xls 格式下 xlrd 的测试结果差异性
如果手头还有历史遗留的 .xls 文件,可以用 xlrd 来读。xlrd 2.0 之后只支持 .xls,不支持 .xlsx,这一点要特别注意。
import xlrd
workbook = xlrd.open_workbook("test_old.xls")
sheet = workbook.sheet_by_name("sales_data")
rows = []
for r in range(sheet.nrows):
rows.append(sheet.row_values(r))
.xls 格式的问题是单元格数量一大,遍历速度会明显降下来。很多开发者在旧系统迁移时发现 Python 读 .xls 比读 .xlsx 慢,这不是错觉,是因为两种格式的底层存储和定位方式不一样。
建议:长期维护的报表尽量转成 .xlsx 或者直接输出 CSV,一方面读取库选择更多,另一方面整体处理生态更现代。
3.3 csv 格式作为对照组
既然标题叫“Excel”,有人会问 CSV 算不算。严格来说不算 Excel 格式,但如果你在做报表导出、数据交换,CSV 可能是更合适的载体。
import csv
start = time.perf_counter()
with open("test_10w_rows.csv", "r", encoding="utf-8") as f:
reader = csv.reader(f)
rows = [row for row in reader]
cost = time.perf_counter() - start
print(f"csv 标准库读取耗时: {cost:.3f} 秒")
CSV 没有格式化样式、公式、单元格类型等复杂结构,本质就是纯文本。所以它的读取速度天然占优势。但要注意,用 Excel 打开 CSV 再另存为 xlsx 后,数据会出现明显的格式推断问题,比如订单号变成科学计数法、日期格式被重排。这块属于业务处理,不属于库性能问题。
3.4 建议记录的数据维度
做对比时不要只记录耗时,至少要同时记录:
- 总耗时
- 文件大小
- 总行数、总列数
- CPU 型号或大致配置
- 内存占用(有条件的情况下看峰值)
- 库版本号
把这些记下来,后续换机器、换数据时就能快速判断耗时增长是数据问题还是环境问题。
4. 数据遍历场景中的性能差异
4.1 读取和遍历要分开看待
很多测试只记录“读取完成”的时间,但在真实业务里,读完之后往往还要做遍历、筛选、聚合、写入新文件这些操作。比如拿到 10 万行 Excel,我们需要按区域统计金额,或者根据备注内容筛选异常单。这时候不同库的数据结构差异就开始影响效率。
pandas 的优势在于,读完之后就可以用向量化操作处理数据,基本不需要写逐行 for 循环。比如按区域分组统计销售额:
df["amount"] = df["quantity"] * df["unit_price"]
result = df.groupby("region")["amount"].sum().reset_index()
print(result)
这类向量化计算量在 10 万行时非常快,通常不到 1 秒。但如果换成 openpyxl 直接读成列表,再用 Python 原生循环去遍历计算,耗时可能达到十几秒甚至更久。
所以如果业务“读取之后还要做数据加工”,强烈建议直接采用 pandas,不要自己在列表数据上写循环。
4.2 逐行处理与高性能场景的取舍
在需要逐行处理的场景,比如清洗脏数据、调用外部接口补充信息、根据规则判断是否命中某条件,pandas 的 iterrows 和普通 for 循环速度都比较慢。原因是 DataFrame 的每一行需要被转换为 Series,这个开销在 10 万行时会被放大。
更稳的做法是先把 DataFrame 转为 list 或 numpy 数组,再做循环。这里有一个对比示例:
# 不推荐的写法:迭代 DataFrame 行
for idx, row in df.iterrows():
total = row["quantity"] * row["unit_price"]
# 更稳的写法:先转 numpy,再遍历
arr = df[["quantity", "unit_price", "amount"]].to_numpy()
total_list = []
for qty, price, amount in arr:
total_list.append(qty * price)
第二种写法虽然也没法打败纯向量化,但在逐行处理场景下明显比 iterrows 快。很多初学者总以为慢是因为读取库不行,实际是遍历方式选错了。
5. 并行读取与批量任务:别把耗时问题变成资源灾难
5.1 要不要用多进程或线程池
遇到几十个 10 万行 Excel 文件,有人第一反应是开线程池、多进程并行读取。这个思路可行,但 Excel 文件的瓶颈一般不仅仅是 CPU,还有磁盘 IO 和内存占用。如果文件存放在机械硬盘上,开太多并行进程反而会让磁盘寻道时间变长,整体耗时不一定下降,甚至可能上升。
建议先把串行读取一遍,记录单文件平均耗时和峰值内存。只有当磁盘整体吞吐还有富余时,才考虑用多进程读取。例如用 concurrent.futures 的 ProcessPoolExecutor 做多文件并行读取:
from concurrent.futures import ProcessPoolExecutor
def read_one(file):
return pd.read_excel(file, sheet_name="sales_data", engine="openpyxl")
files = ["a.xlsx", "b.xlsx", "c.xlsx"]
with ProcessPoolExecutor(max_workers=3) as executor:
results = list(executor.map(read_one, files))
如果单个文件读取时内存占用已经接近 2GB 到 3GB,并行任务就要非常克制。10 万行 xlsx 在 pandas 中转成 DataFrame 后,内存通常要占用几百 MB,多个文件同时加载可能直接吃掉 8GB 到 16GB 内存。
5.2 批量处理时的失败重试与命名策略
批量场景里,能不能稳定跑完比单个文件跑多快更重要。建议顺序是:
- 先写一个函数,只处理单文件,入参是文件路径,出参是统计结果或写好的新文件。
- 单独用 3 个文件做冒烟测试,确认路径、编码、输出目录没问题。
- 再跑全量,打印文件处理进度。
- 单个文件报错时,记录错误信息并跳过,不要中断整个任务。
一个常见的坑是输出文件命名冲突。如果用原文件名加固定后缀,多个并行任务同时写文件时可能出现覆盖或写入失败。建议用“原文件名 + 时间戳 + 随机短码”的方式避免冲突,并且中间结果统一写到独立目录。
6. 从耗时结果倒推选型建议
6.1 低频小文件场景:怎么选都不太重要
如果你的任务是每天只读几千行 Excel,或者只是偶尔帮业务同事跑一次数据,那直接用 pandas 就可以了,不用纠结底层库。
6.2 十万行以上常规表格:优先 pandas
10 万行、几十列以内的常规表格,pandas 读取虽然没有特别极致,但读写之后的处理效率很高。真正慢的部分往往是在业务层用了不合适的循环,而不是读取本身。
6.3 只读取不处理:考虑 read_only + 手工迭代
如果你的场景只是把 xlsx 里的数据抽出来,转存到数据库或者另一个文件,不要求做复杂计算,那么直接使用 openpyxl 的 read_only 模式并按行迭代,可能比 pandas 更省内存,也更容易控制节奏。
6.4 超大文件或生产级方案:考虑 csv 或换格式
当文件行数超过几十万、甚至上百万行时,xlsx 格式本身会越来越吃力。此时更合理的方向不是继续遴选 Python 库,而是把上游数据导出为 CSV,或用专业的列式存储格式。这不是库的问题,是场景和格式的边界问题。
7. 常见报错与耗时排查链路
7.1 读取慢的通用排查步骤
如果发现某个库读取 10 万行 Excel 特别慢,先别急着下结论说这个库不行。按下面的顺序排查:
- 先看文件后缀,确认是 xlsx、xls 还是 csv。
- 再看文件大小,如果 10 万行文件有几百 MB,说明里面可能包含大量内嵌图片、隐藏工作表、样式或格式冗余。
- 确认是否开启了隐藏对象或加密工作表。有些 Excel 文件会因保护、缓存等因素导致解析变慢。
- 检查 Python 环境是否是 32 位,32 位进程内存访问上限会影响大文件处理。
- 最后才考虑换库或调参。
7.2 常见报错原因与处理
ValueError: Excel file format cannot be determined:文件后缀和真实格式不一致,按实际格式匹配对应引擎。ModuleNotFoundError: No module named 'openpyxl':底层引擎缺失,安装对应依赖。MemoryError:峰值内存过高,改用 read_only 模式或分批读取。- 读取后日期变成数字:Excel 日期序列值常见问题,读取时不要急着处理,先看原始数据。
- 订单号或长数字变成科学计数法:在 Excel 中查看时出现,读到 pandas 后数字类型可能被转换,需要将列转成字符串并保留位数。
7.3 卡住时的正确观察方式
如果读取文件时卡住,先确认不是真的报错,只是没打进度日志。常见做法是打开任务管理器或资源监视器,看 CPU、内存、磁盘读写是否还在活动。如果 CPU 一直是单核 100%,说明解析过程还在进行;如果磁盘读写和 CPU 都基本为 0,那可能是卡死在等待某个外部资源上,比如网络盘或杀毒软件扫描。
读取网络共享目录上的 Excel 时,建议先复制到本地临时目录再解析,网络文件锁和延迟会成为隐藏耗时项。
8. 留下几个关键参考点
如果让我给一个比较务实的启动配置,通常会这样:
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 常规数据分析 | pandas + openpyxl 引擎 | API 友好,读取后处理效率高 |
| 只读取不加工 | openpyxl read_only 模式 | 内存占用更低 |
| 历史 .xls 文件 | xlrd | 现阶段只能靠它 |
| 大批量导出或交换 | csv | 解析代价最低 |
在你自己的环境里,建议把“读取数据、转换为可用结构、完成一次简单加工”三者连起来计时,而不是只测读取 API 的返回时间。这样得到的数据才更像真实业务耗时。
最后再强调几个容易踩的坑:不要一上来就用线程池读 Excel,先看磁盘和内存;不要在循环里面频繁打开和关闭工作簿,能一次读完就一次读完;不要以为用了 pandas 就一定快,如果数据后处理写了很多原生 for 循环,整体耗时可能比 openpyxl 直接读还糟糕。
按我自己的习惯,拿到一个 Excel 处理任务,第一件事是先看文件样例和行数,第二件事是确认输出要求,第三件事才选定读取库。先把入口和出口弄清楚,中间的耗时才能优化到点子上。
到此这篇关于Python利用pandas与openpyxl读取10万行Excel的耗时实测的文章就介绍到这了,更多相关Python pandas与openpyxl读取Excel内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
