Python怎么用openpyxl逐行读取Excel构建字典

周予安
周予安
发布时间:2026-11-01| 5人浏览过
原创

详细讲解如何使用Python的openpyxl库逐行读取Excel文件,将每一行数据转换为字典,涵盖安装、核心代码、多种构建方式及常见问题处理。

用openpyxl逐行读取Excel并构建字典,核心思路只有一句话:先把第一行当作字典的键,然后遍历剩余行,把每行的单元格值和键一一对应,组成一个字典。整个过程不超过十行代码,但实际使用时有几个细节值得留意——空行处理、数据类型保持、大文件性能,这篇文章会逐一说清楚。

安装openpyxl

openpyxl是专门处理.xlsx格式的第三方库,不支持旧的.xls格式。如果你的环境里还没装过,用pip安装即可:

pip install openpyxl

安装完成后在Python交互环境中执行import openpyxl,不报错就说明可以用了。如果你使用的是Anaconda环境,openpyxl通常已经预装,可以跳过这一步。

理解Excel在openpyxl中的数据结构

在写代码之前,有必要弄清楚openpyxl怎么看待一个Excel文件。openpyxl把一个.xlsx文件加载为Workbook对象,Workbook包含若干Worksheet,每个Worksheet是一张表。调用ws.iter_rows()可以按行遍历,每行返回一个由Cell对象组成的元组。Cell对象的.value属性就是单元格里的实际值,openpyxl会自动将数字、日期等保持为Python对应的类型,而不是统一变成字符串。

Python怎么用openpyxl逐行读取Excel构建字典
图1:openpyxl 加载 Excel 后的对象层级,从 Workbook 到单元格值

理解了这个层级关系,后面的代码就很好理解了:我们只需要在行这一层操作,用第一行的值做键,后续行的值做值。

最基本的写法:逐行构建字典列表

假设有一个Excel文件students.xlsx,第一行是表头姓名、年龄、城市、分数,从第二行开始是数据。我们要把每行数据变成一个字典,最终得到一个字典列表。

from openpyxl import load_workbook

wb = load_workbook('students.xlsx')
ws = wb.active

rows = ws.iter_rows(values_only=True)
headers = next(rows)          # 取出第一行作为键

result = []
for row in rows:
    record = dict(zip(headers, row))
    result.append(record)

print(result)

假设Excel内容如下:

| 姓名   | 年龄 | 城市   | 分数 |
|--------|------|--------|------|
| 张三   | 28   | 北京   | 85.5 |
| 李四   | 24   | 上海   | 92.0 |
| 王五   | 31   | 广州   | 78.3 |

运行上面的代码,输出结果(示例):

[
  {'姓名': '张三', '年龄': 28, '城市': '北京', '分数': 85.5},
  {'姓名': '李四', '年龄': 24, '城市': '上海', '分数': 92.0},
  {'姓名': '王五', '年龄': 31, '城市': '广州', '分数': 78.3}
]

几个关键点值得注意:

  • values_only=True让iter_rows()直接返回值的元组,而不是Cell对象的元组,代码更简洁。
  • next(rows)从迭代器中取出第一行。由于iter_rows()返回的是生成器,取出第一行后,后续的for循环自然从第二行开始。
  • zip(headers, row)把表头和数据一一配对,再用dict()转成字典,这是Python里非常地道的写法。

处理空行和缺失值

实际的Excel文件经常不那么干净。有时候中间会夹杂空行,有时候某些单元格没有填值。如果不做处理,空行会产生所有值都是None的字典,混在结果里会给后续处理添麻烦。

一个简单的办法是在循环中加一个判断——如果一整行的值全是None,就跳过:

result = []
for row in rows:
    if all(v is None for v in row):
        continue
    record = dict(zip(headers, row))
    result.append(record)

对于个别单元格为空的情况,构建出的字典里对应的值就是None。这在大多数场景下是合理的,因为None在Python中明确表示"没有值"。如果你需要把它替换为空字符串或默认值,可以用字典推导式:

record = {k: (v if v is not None else '') for k, v in zip(headers, row)}

用某一列的值做字典的键

上面的方式得到的是一个列表,用索引访问特定记录。但有时候我们更希望用某个字段——比如姓名——直接定位到对应的数据行。这时候可以构建一个以某列值为键、以整行字典为值的嵌套字典:

wb = load_workbook('students.xlsx')
ws = wb.active

rows = ws.iter_rows(values_only=True)
headers = next(rows)

data = {}
for row in rows:
    if all(v is None for v in row):
        continue
    record = dict(zip(headers, row))
    key = record['姓名']
    data[key] = record

# 直接通过姓名访问
print(data['李四'])
# 输出(示例): {'姓名': '李四', '年龄': 24, '城市': '上海', '分数': 92.0}

这里有一个前提:作为键的那一列不能有重复值,否则后出现的行会覆盖前面的。如果确实存在重复,可以改成列表收集:

from collections import defaultdict

data = defaultdict(list)
for row in rows:
    if all(v is None for v in row):
        continue
    record = dict(zip(headers, row))
    data[record['城市']].append(record)

# data['北京'] 是一个列表,包含所有城市为北京的记录
Python怎么用openpyxl逐行读取Excel构建字典
图2:逐行读取 Excel 构建字典列表的完整操作流程

只读取特定列或特定行范围

iter_rows()接受min_row、max_row、min_col、max_col参数,可以精确控制读取范围。比如表格有十几列但你只关心前四列,或者前几行是说明文字、真正的表头从第三行开始,都可以通过这些参数调整。

# 只读取第3行到第100行,第1到第4列
rows = ws.iter_rows(min_row=3, max_row=100,
                    min_col=1, max_col=4,
                    values_only=True)
headers = next(rows)  # 第3行作为表头

需要注意的是,min_row和max_row用的是Excel中的行号(从1开始),不是Python索引。如果指定了max_row但实际数据没有那么多行,多出来的行会返回全None的元组,所以前面提到的空行过滤在这种场景下尤其有用。

处理大文件时的性能考虑

openpyxl默认会把整个工作簿加载到内存。对于几千行的文件这完全没问题,但如果文件有几十万行、几百MB,内存占用会很大。openpyxl为此提供了read_only模式:

wb = load_workbook('big_file.xlsx', read_only=True)
ws = wb.active

rows = ws.iter_rows(values_only=True)
headers = next(rows)

for row in rows:
    record = dict(zip(headers, row))
    # 逐行处理,不必一次性存入列表
    process(record)

wb.close()  # read_only模式下用完需要手动关闭

read_only=True使openpyxl以流式方式读取,内存占用大幅降低。但这个模式有两个限制:不能修改工作表,也不能随机访问单元格(比如ws['A5']会报错)。另外用完后必须调用wb.close()释放文件句柄,或者用with语句:

# 也可以用 contextmanager 的方式(openpyxl 3.0+ 支持)
# 但更稳妥的做法是显式关闭
如果你处理的是.xls格式的旧文件,openpyxl无法打开,需要改用xlrd库。两者的API不同,不能直接替换。
Python怎么用openpyxl逐行读取Excel构建字典
图3:普通加载与 read_only 模式在不同数据量下的内存占用对比

把构建过程封装成函数

如果这个操作要反复用到,封装成函数是自然的选择。下面这个函数支持指定Sheet名称、选择用哪一列做字典键、以及是否启用只读模式:

from openpyxl import load_workbook

def excel_to_dicts(filepath, sheet_name=None, key_column=None, read_only=False):
    """
    读取Excel文件,将每行数据转为字典。

    参数:
        filepath: xlsx文件路径
        sheet_name: 工作表名称,默认读取活动工作表
        key_column: 如果指定,返回以该列值为键的嵌套字典;否则返回列表
        read_only: 是否使用只读模式(适合大文件)

    返回:
        list[dict] 或 dict[str, dict]
    """
    wb = load_workbook(filepath, read_only=read_only)
    ws = wb[sheet_name] if sheet_name else wb.active

    rows = ws.iter_rows(values_only=True)
    headers = next(rows)

    if key_column:
        result = {}
        for row in rows:
            if all(v is None for v in row):
                continue
            record = dict(zip(headers, row))
            result[record[key_column]] = record
    else:
        result = [
            dict(zip(headers, row))
            for row in rows
            if not all(v is None for v in row)
        ]

    if read_only:
        wb.close()

    return result

调用示例:

# 返回字典列表
students = excel_to_dicts('students.xlsx')

# 以姓名为键
students_by_name = excel_to_dicts('students.xlsx', key_column='姓名')

# 读取指定Sheet,启用只读模式
data = excel_to_dicts('big_data.xlsx', sheet_name='2024年数据', read_only=True)

和其他方案的比较

逐行读取Excel构建字典这件事,除了openpyxl手写之外还有其他选择。pandas的read_excel()可以一步到位地把Excel读成DataFrame,然后用.to_dict(orient='records')转成字典列表。代码更短,但pandas是一个很重的依赖,如果你的项目只需要读取Excel构建字典,单独引入pandas有点浪费。

另一个选择是csv模块的DictReader——如果你的数据源可以先另存为CSV的话。DictReader天然就是用第一行做键、逐行返回字典,但它处理不了Excel的多Sheet、数据类型(CSV里所有值都是字符串)和格式信息。

openpyxl手写的优势在于:依赖轻、控制精细、保留数据类型。当你明确只需要把Excel行变成字典,并且希望精确控制读取范围和异常处理时,这种方式最合适。

容易踩的几个坑

在实际项目中用这套方法,有几个问题值得提前知道:

表头有合并单元格。openpyxl读取合并单元格时,只有左上角的单元格有值,其余为None。如果你的表头存在合并,zip出来的字典键会出现None。解决方法是在拿到headers后检查并补全,或者在Excel端取消合并后再处理。

日期列被读成datetime对象。openpyxl会把Excel中日期格式的单元格自动解析为Python的datetime对象。如果你需要字符串格式,在构建字典时可以手动转换:

from datetime import datetime

value = cell_value
if isinstance(value, datetime):
    value = value.strftime('%Y-%m-%d')

数字列被读成浮点数。Excel中整数在openpyxl里有时会被读为int,有时会被读为float(比如"28"可能变成28.0),这取决于Excel内部的存储方式。如果你需要确保是整数,可以在构建字典时判断并转换。

文件被其他程序占用。如果Excel文件正在被WPS或Microsoft Excel打开,openpyxl可能无法读取或读取到不完整的数据。处理前确保文件没有被其他程序锁定。

Python怎么用openpyxl逐行读取Excel构建字典
图4:openpyxl 逐行构建字典的核心知识点总结

完整可运行示例

最后,把前面讲的内容整合成一个完整的、可以直接运行的脚本。这个脚本先创建一个示例Excel文件,然后演示三种读取方式:

from openpyxl import Workbook, load_workbook

# ---- 第一步:创建示例文件 ----
wb_new = Workbook()
ws_new = wb_new.active
ws_new.append(['姓名', '年龄', '城市', '分数'])
ws_new.append(['张三', 28, '北京', 85.5])
ws_new.append(['李四', 24, '上海', 92.0])
ws_new.append(['王五', 31, '广州', 78.3])
ws_new.append(['赵六', 27, '深圳', 88.1])
wb_new.save('demo_students.xlsx')
print('示例文件已创建: demo_students.xlsx')

# ---- 第二步:读取为字典列表 ----
wb = load_workbook('demo_students.xlsx')
ws = wb.active
rows = ws.iter_rows(values_only=True)
headers = next(rows)

student_list = [
    dict(zip(headers, row))
    for row in rows
    if not all(v is None for v in row)
]

print('\n字典列表:')
for s in student_list:
    print(f"  {s}")

# ---- 第三步:以姓名为键的字典 ----
wb2 = load_workbook('demo_students.xlsx')
ws2 = wb2.active
rows2 = ws2.iter_rows(values_only=True)
headers2 = next(rows2)

student_dict = {}
for row in rows2:
    if all(v is None for v in row):
        continue
    record = dict(zip(headers2, row))
    student_dict[record['姓名']] = record

print('\n通过姓名查找:')
print(f"  李四 → {student_dict['李四']}")

# ---- 第四步:只读取特定列 ----
wb3 = load_workbook('demo_students.xlsx')
ws3 = wb3.active
rows3 = ws3.iter_rows(min_col=1, max_col=2, values_only=True)
headers3 = next(rows3)

name_age = [dict(zip(headers3, row)) for row in rows3]
print('\n只取姓名和年龄:')
for item in name_age:
    print(f"  {item}")

运行后你会看到三种方式的输出结果(示例),确认整个流程是通的之后,就可以根据自己的实际文件路径和列名去修改了。

总结一下:openpyxl逐行读取Excel构建字典,本质上就是iter_rows取行、next取表头、zip配对、dict收尾。在这个基础上,根据实际需要加上空行过滤、列范围控制、只读模式和键列选择,就能应对绝大多数场景。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。