前言
学 Excel 自动化的人几乎都会在 openpyxl 和 xlwings 之间犹豫一下。两者都能读写 .xlsx,于是很多人默认「xlwings 是 openpyxl 的升级版」,随便挑一个用。这个理解是错的,而且错得很贵------选错之后,你会对着一个莫名其妙的报错查半天,最后发现是根本没装 Excel。
真实的分野是这样的:openpyxl 是一个纯 Python 的 xlsx 读写器 ,它直接操作文件里的 XML,不需要 Excel 参与;xlwings 是一个Excel 进程的遥控器,它通过 COM 自动化接口去驱动本机真实的 Excel 程序,你写的每一行代码最终都是在命令 Excel 做事。官方文档里写得很直白:xlwings 开源版需要本机安装 Excel,因此只能在 Windows 和 macOS 上工作。
理解了这一点,适用场景就清楚了:需要真实计算引擎(公式要真算)、需要录制式的界面操作、需要跑 VBA 宏、需要把 Python 当宏语言嵌进 Excel------用 xlwings;只是批量生成或读取数据文件------用 openpyxl 更快更省事。本文把 xlwings 的核心用法、生命周期管理和典型踩坑讲透,具体参数与返回值以 xlwings 官方文档为准。
一、安装与运行前提
bash
pip install xlwings
除了包本身,还有三条硬前提,缺一条代码就跑不起来:
| 前提 | 说明 | 不满足时的表现 |
|---|
|------------|------------------|-------------|
| 本机安装 Excel | 开源版依赖 Excel 应用本身 | 抛异常,提示找不到应用 |
|---------------------|-------------------------|--------|
| 系统为 Windows 或 macOS | Linux 不支持(除非只用只读的文件读取器) | 无法启动应用 |
|-------------|----------------------------|--------------|
| Python 版本达标 | 官方文档写明当前版本至少要求 Python 3.11 | 安装或导入阶段就可能报错 |
注意最后一行的版本要求是针对当前版本的。早期版本支持更低的 Python,如果你的环境版本偏旧,请以官方文档中对应版本的说明为准。
另外,官方文档提到 xlwings 在 Anaconda 里是预装的,用 Anaconda 的话可以先确认一下是否已经存在,别重复装。要开发 UDF(自定义函数)或想在 Excel 里点按钮调用 Python,才需要装插件,命令是 xlwings addin install;如果只是「从 Python 主动去操作 Excel」,不需要插件。
二、连接工作簿:三种进入方式
python
# 适用于 Python 3.11+
# 需要先安装:pip install xlwings
# 运行前提:本机已安装 Microsoft Excel
# 具体参数与返回值以 xlwings 官方文档为准
import xlwings as xw
# 方式一:新建一个空白工作簿
wb = xw.Book()
# 方式二:连接一个已经打开、或位于当前工作目录的文件
wb = xw.Book("报表.xlsx")
# 方式三:给完整路径。Windows 路径含反斜杠,必须用原始字符串
wb = xw.Book(r"C:\data\报表.xlsx")
# 取工作表:按名字或按索引(索引从 0 开始)
sheet = wb.sheets["Sheet1"]
first = wb.sheets[0]
# 用完关掉,否则 Excel 进程会一直挂着
wb.close()
第三个方式里的原始字符串 r"..." 不是可选项。Windows 路径里 \t、\n、\b 都是转义序列,r 前缀让反斜杠保持字面含义。
如果一个同名文件在两个 Excel 实例里都开着,就必须连实例一起限定。每个实例有一个进程 ID,可以用 xw.apps.keys() 查出来,然后按 ID 取:
python
# 适用于 Python 3.11+
# 需要先安装:pip install xlwings
# 具体参数与返回值以 xlwings 官方文档为准
import xlwings as xw
print(list(xw.apps.keys())) # 打印所有 Excel 实例的进程 ID
pid = list(xw.apps.keys())[0]
wb = xw.apps[pid].books["报表.xlsx"] # 精确定位到某个实例里的工作簿
sheet = wb.sheets[0]
三、读写单元格与区域
区域的定位方式有好几种,官方文档里给出的这些写法是等价的,选顺手的即可:
python
# 适用于 Python 3.11+
# 需要先安装:pip install xlwings
# 具体参数与返回值以 xlwings 官方文档为准
import xlwings as xw
wb = xw.Book(r"C:\data\报表.xlsx")
sheet = wb.sheets[0]
# 以下写法都指向同一个区域 A1
sheet.range("A1")
sheet["A1"]
sheet.range((1, 1)) # 元组形式用的是 Excel 的 1 起算索引
sheet[0, 0] # 这种下标形式则是 0 起算
# 整块区域
sheet["A1:C3"]
sheet.range((1, 1), (3, 3))
sheet[0:3, 0:3]
读写的核心只有 .value 一个属性:
python
# 适用于 Python 3.11+
# 需要先安装:pip install xlwings
# 具体参数与返回值以 xlwings 官方文档为准
import xlwings as xw
wb = xw.Book(r"C:\data\报表.xlsx")
sheet = wb.sheets[0]
# 单个单元格:读回来是标量
sheet["A1"].value = "销售额"
print(sheet["A1"].value)
# 整块写入:二维列表会按行列铺开
sheet["A2"].value = [
["一月", 100],
["二月", 200],
]
# expand() 从起点向外扩展,自动找到相邻的数据边界
block = sheet["A2"].expand().value
print(block) # [['一月', 100.0], ['二月', 200.0]]
# 保存到新路径;不给参数则按原路径保存
wb.save(r"C:\data\报表_已更新.xlsx")
wb.close()
expand() 是 xlwings 很好用的一个特性:你只要给出左上角,它会自动把连着的数据区域框出来。它依赖「数据是连续块」这个假设,块中间有空行就会提前停下。
对做数据分析的人还有一个便利:options 可以把区域直接按 Pandas 的 DataFrame 或 NumPy 数组来读写,来回转换由库负责:
python
# 适用于 Python 3.11+
# 需要先安装:pip install xlwings 以及 pandas
# 具体参数与返回值以 xlwings 官方文档为准
import xlwings as xw
import pandas as pd
wb = xw.Book(r"C:\data\报表.xlsx")
sheet = wb.sheets[0]
df = pd.DataFrame([[1, 2], [3, 4]], columns=["a", "b"])
sheet["A1"].value = df # 写进去
back = sheet["A1"].options(pd.DataFrame, expand="table").value # 读回来
print(back)
wb.close()
四、生命周期管理:一定要用上下文管理器
这是 xlwings 最容易出问题的地方。Excel 是一个外部进程,Python 脚本结束时它不会自动退出。如果你反复运行脚本却不关闭,任务管理器里会堆积一堆 EXCEL.EXE。
官方文档给出的做法是用 with 语句:
python
# 适用于 Python 3.11+
# 需要先安装:pip install xlwings
# 具体参数与返回值以 xlwings 官方文档为准
import xlwings as xw
# with 退出时会负责把这个 Excel 实例清干净
with xw.App() as app:
book = app.books.add() # 在当前实例里新建工作簿
sheet = book.sheets[0]
sheet["A1"].value = "由 xlwings 写入"
book.save(r"C:\data\输出.xlsx")
book.close()
如果不用上下文管理器、直接 xw.App(),就一定要在 finally 里显式调用 app.quit()。App 对象还提供了一些控制行为的属性,比如 visible 控制窗口是否显示、display_alerts 控制是否弹确认框、screen_updating 控制屏幕刷新。批量写入前把屏幕刷新关掉是很常见的做法,官方文档也特别提醒:脚本结束时别忘了把 screen_updating 恢复成 True,否则那个 Excel 实例会一直卡在「不刷新」的状态。
常见坑点
- 把 xlwings 当纯数据处理工具用
❌ 用 xlwings 逐格读取几十万行来做统计,运行缓慢且必须开着 Excel。 ✅ 纯数据读写优先用不带界面的库;只有当任务确实需要 Excel 的计算引擎、宏或界面交互时才上 xlwings。
- 本机没装 Excel 却直接 import 后调用
❌ 在没装 Excel 的机器(或 Linux 服务器)上跑 xw.Book(...),脚本直接抛异常。 ✅ 部署前先确认目标机是否装 Excel;如果只是要在服务器上生成 xlsx,应当换用不需要 Excel 的库。
- 目标文件正被 Excel 打开时写入
❌ 用户手动打开了「报表.xlsx」没关,脚本又去写同一个文件,出现保存失败或内容互相覆盖。 ✅ 写入前确认文件未被占用;批量任务输出到新文件名,或者干脆用无人值守的独立 Excel 实例处理。
- Windows 路径不用原始字符串
❌ xw.Book("C:\data\new\报表.xlsx") 里的 \n、\t 被当成转义字符,路径变成乱码。 ✅ 一律写成 xw.Book(r"C:\data\new\报表.xlsx"),或者用正斜杠 C:/data/new/报表.xlsx。
- 忘记关闭,Excel 进程越堆越多
❌ 每次运行脚本都 xw.App() 或 xw.Book() 却不 close/quit,任务管理器里残留大量 EXCEL.EXE。 ✅ 用 with xw.App() as app: 管理实例;不用上下文管理器时把 quit() 放进 finally。
- 以为
visible=False就等于不消耗资源
❌ 认为把窗口隐藏起来就变成轻量操作,于是拿它做大批量数据处理。 ✅ 隐藏只影响窗口是否显示,Excel 进程依然完整存在、依然在计算。重量级任务换方案。
- 把
expand()当成「找到所有数据」的万能方法
❌ 表格中间有一个空行,expand().value 只返回了前半截,后面的数据被静默丢掉。 ✅ 记住 expand 是从起点向外找连续块,遇到空行就停;数据不连续时改用明确的区域或先取整个已用区域再自行处理。
总结
| 场景 | 选谁 | 原因 |
|---|
|--------------|------|--------------------|
| 批量生成/读取 xlsx | 纯文件库 | 无需 Excel,快且可部署到服务器 |
|----------------|---------|-----------|
| 必须让 Excel 真算公式 | xlwings | 用的是真实计算引擎 |
|---------------|---------|---------------|
| 跑 VBA 宏、界面自动化 | xlwings | 它本来就是遥控 Excel |
|---------------|----------|-------------|
| 只读文件、不启 Excel | 只读的文件读取器 | 不依赖本机 Excel |
xlwings 的价值不在于「能读写 Excel」,而在于「能驱动 Excel」。把它用在对的位置------需要真实 Excel 参与的场景------它是利器;用错位置则既慢又难部署。使用前先确认本机 Excel 与 Python 版本满足要求,代码里用上下文管理器管好进程,其余具体参数与返回值请以 xlwings 官方文档为准。