如何使用openpyxl读取Excel单元格的值而不是计算它的公式?

141
我正在使用openpyxl来读取一个单元格的值(Excel插件-Web服务更新了这一列)。
我已经使用了data_only = True,但它没有显示当前的单元格值,而是显示了上次Excel读取表格时存储的值。
wbFile = openpyxl.load_workbook(filename = xxxx, data_only=True)
wsFile = wbFile[c_sSheet]

如何读取实际单元格的值?

10
我认为openpyxl不能复制Excel的庞大公式计算子系统,因此我认为你无法让它自动重新计算所有公式。最好的情况是,你可以为该大型公式语言的子集实现自己的基于eval的解释器。 - Alex Martelli
我不需要计算或执行公式,我只需要读取单元格的值。 - user3411047
6
“单元格的值”(超出公式范围)是指当 Excel 保存工作表时保存的内容,而您暗示这并不是您想要的——您想要“当前”的值(无疑取决于其他单元格中的值),这意味着您需要根据其他单元格的当前值重新计算公式! - Alex Martelli
抱歉让您感到困惑,基本上我的列是通过Excel插件更新的(Web服务将轮询数据)...我需要提取该列单元格的值。 - user3411047
6
我认为你需要采取以下两种措施之一:(A)再次使用Excel来重新计算公式,可以通过所述的插件或类似的http://xlwings.org/等来实现;或者(B)如果公式足够简单,可以按照我说的实现自己的公式解释器。`openpyxl`和其他无需Excel的Excel文件阅读器将无法执行所需的公式计算(无论你认为你需要它还是不需要它:-)。 - Alex Martelli
这个回答解决了你的问题吗?如何使用openpyxl模块来访问单元格的真实值 - Josh Correia
10个回答

267
wb = openpyxl.load_workbook(filename, data_only=True)

data_only 标志有所帮助。


8
是的,这回答了问题。如果你想同时访问方程和值,那么你可能会得到两个实例,比如 wb2 = openpyxl.load_workbook(filename)。然后在读取相应的索引时,你从 wb 中得到值,从 wb2 中得到方程。 - Ajeeb.K.P
1
嗨,该单元格不是空的。这是因为openpyxl不会计算公式。我通过将Excel数据取出并在服务器上进行所有计算来解决了这个问题 :| - galeej
可能值得在官方功能跟踪器https://bitbucket.org/openpyxl/openpyxl/issues上添加一个问题,但根据https://bitbucket.org/openpyxl/openpyxl/issues/291/reading-cell-values-with-formula-is-not的判断,我怀疑这是一个有意避免的功能:编辑公式将难以与计算出的值同步。您必须重新实现整个公式评估系统。这可能会侵犯版权,需要Excel(或OpenOffice)许可证,或将工作簿限制为有效的只读。 - Sarah Messer
4
当设置data_only为True时,cell.value将返回Excel在写入文件时所知道的值。cell.internal_value将返回公式。 - Burtski
2
我遇到了一些奇怪的行为:当使用 data_only=True 并尝试读取带有公式的单元格时(对于静态单元格或未使用 data_only=True 的单元格正常工作),无论是 cell.value 还是 cell.internal_value 都返回 None。有什么想法吗? - Jean-Francois T.
显示剩余3条评论

21
正如@alex-martelli所说,openpyxl不会计算公式。当你使用openpyxl打开一个Excel文件时,你可以选择读取公式或最后计算的值。如果像你所指示的那样,公式依赖于插件,那么缓存的值永远无法准确。由于插件在文件规范之外,它们将永远不会得到支持。相反,你可能想要看一下类似xlwings这样的工具,它可以与Excel运行时进行交互。

2
这个答案有两个问题:(1)你如何区分打开XL文件以读取公式和读取最后计算值?这是通过data_only=True参数实现的吗?(2)在现实世界中,“last calculated value”是什么意思?也就是说,如果在上次(手动/人为)更改后保存了XL文件,这是否意味着所有单元格都被重新计算/处于它们的“last calculated value”?通常何时重新计算单元格的值?(我知道这更多是一个Excel问题而不是OpenPyXL,但感谢您的澄清) - cssyphus
2
没事了,我在这里找到了答案:https://dev59.com/Y1oV5IYBdhLWcg3wi_SY - cssyphus

17

data_only:读取公式单元格的值。

keep_vba:仅在使用启用宏的Excel时使用。

file_location = 'C:\Arpan Saini\Monsters\Project_Testing\SecCardGrad\SecCardGrad_Latest_docs\Derived_Test_Cases_Secure_Card_Graduate.xlsm'
wb = load_workbook(file_location, keep_vba=True, data_only=True)

12

如@Charlie Clark所提到的,如果你有MS Excel,你可以使用xlwings。这里是一个例子:

假设您有一个带有公式的Excel表格,为此我使用openpyxl定义了一个示例。

from openpyxl import Workbook, load_workbook
wb=Workbook()

ws1=wb['Sheet']

ws1['A1']='a'
ws1['A2']='b'
ws1['A3']='c'

ws1['B1']=1
ws1['B2']=2
ws1['B3']='=B1+B2'

wb.save('to_erase.xlsx')

如前所述,如果我们使用openpyxl重新加载Excel文件,我们将无法获得已计算的公式。

wb2 = load_workbook(filename='to_erase.xlsx',data_only=True)
wb2['Sheet']['B3'].value

你可以使用xlwings来获取Excel计算的公式:

import xlwings as xw
wbxl=xw.Book('to_erase.xlsx')
wbxl.sheets['Sheet'].range('B3').value

该函数返回预期的值3。

当处理具有非常复杂的公式和工作表之间的引用的电子表格时,我发现它非常有用。


1
似乎只能在Windows上运行。xlwings无法在Linux上安装。 - Juergen
2
实际上,这些公式是由MS Excel评估的。我认为这个解决方案只适用于安装了此软件的计算机(虽然该库应该可以在Mac上运行)。 - Nabla

8
面对相同的问题,需要读取单元格的值,无论这些单元格是标量、预先计算过的公式还是未计算的公式,都希望具有容错性而非正确性。
策略很简单:
1. 如果单元格不包含公式,则返回单元格的值; 2. 如果是公式,则尝试获取其预计算值; 3. 如果无法获取,则尝试使用 pycel 进行评估; 4. 如果失败(由于 pycel 对公式的支持有限或出现某些错误),则发出警告并返回 None。
我创建了一个类,隐藏了所有这些机制,并提供了简单的接口来读取单元格的值。
很容易修改该类,以便在第4步上抛出异常,如果更看重正确性而非容错性。
希望它能帮助到某些人。
from traceback import format_exc
from pathlib import Path
from openpyxl import load_workbook
from pycel.excelcompiler import ExcelCompiler
import logging


class MESSAGES:
    CANT_EVALUATE_CELL = ("Couldn't evaluate cell {address}."
                          " Try to load and save xlsx file.")


class XLSXReader:
    """
    Provides (almost) universal interface to read xlsx file cell values.

    For formulae, tries to get their precomputed values or, if none,
    to evaluate them.
    """

    # Interface.

    def __init__(self, path: Path):
        self.__path = path
        self.__book = load_workbook(self.__path, data_only=False)

    def get_cell_value(self, address: str, sheet: str = None):
        # If no sheet given, work with active one.
        if sheet is None:
            sheet = self.__book.active.title

        # If cell doesn't contain a formula, return cell value.
        if not self.__cell_contains_formula(address, sheet):
            return self.__get_as_is(address, sheet)

        # If cell contains formula:
        # If there's precomputed value of the cell, return it.
        precomputed_value = self.__get_precomputed(address, sheet)
        if precomputed_value is not None:
            return precomputed_value

        # If not, try to compute its value from the formula and return it.
        # If failed, report an error and return empty value.
        try:
            computed_value = self.__compute(address, sheet)
        except:
            logging.warning(MESSAGES.CANT_EVALUATE_CELL
                            .format(address=address))
            logging.debug(format_exc())
            return None
        return computed_value                

    # Private part.

    def __cell_contains_formula(self, address, sheet):
        cell = self.__book[sheet][address]
        return cell.data_type is cell.TYPE_FORMULA

    def __get_as_is(self, address, sheet):
        # Return cell value.
        return self.__book[sheet][address].value

    def __get_precomputed(self, address, sheet):
        # If the sheet is not loaded yet, load it.
        if not hasattr(self, '__book_with_precomputed_values'):
            self.__book_with_precomputed_values = load_workbook(
                self.__path, data_only=True)
        # Return precomputed value.
        return self.__book_with_precomputed_values[sheet][address].value

    def __compute(self, address, sheet):
        # If the computation engine is not created yet, create it.
        if not hasattr(self, '__formulae_calculator'):
            self.__formulae_calculator = ExcelCompiler(self.__path)
        # Compute cell value.
        computation_graph = self.__formulae_calculator.gen_graph(
            address, sheet=sheet)
        return computation_graph.evaluate(f"{sheet}!{address}")

你提到了 pycel。多么棒的想法! - Rockallite
@Rockallite 你应该知道,pycel仅支持有限的Excel函数集。但对于简单的情况,它可以很好地工作。 - krvkir
这里有几个问题:TYPE_FORMULA 应该是 from openpyxl.cell.cell import TYPE_FORMULA,gen_graph() 已经不再可用 - 直接使用 evaluate()。 - advance512

7
我用以下方法解决了这个问题:
import xlwings
from openpyxl import load_workbook

data = load_workbook('PATH_TO_YOUR_XLSX_FILE')
data['sheet_name']['A1'].value = 1
data.save('PATH_TO_YOUR_XLSX_FILE')

excel_app = xlwings.App(visible=False)
excel_book = excel_app.books.open('PATH_TO_YOUR_XLSX_FILE')
excel_book.save()
excel_book.close()
excel_app.quit()

data = load_workbook('PATH_TO_YOUR_XLSX_FILE', data_only=True)

我希望这可以帮助您...


1

不要使用openpyxl,而是使用xlwings。


0
我发现如果工作表中存在“REF!”错误单元格,则 data_only 选项无法正常工作。 Openpyxl 返回我微小测试 xlsx 文件中每个单元格值的 None。 对我来说,在打开 Excel 并修复单元格后,data_only 就可以完美工作了。 我使用的是 openpyxl 3.0.3。

0

与其使用 Python 库执行 Excel 计算,我让 Excel 自己完成。

为什么?虽然不是纯 Python,但它最大程度地减少了涉及的 Python 量。我不使用 Python 来计算 Excel 公式,而是让 Excel 处理自己的功能。这避免了评估 Excel 公式的 Python 中可能存在的任何错误。 以下是该方法的概要:

  1. 使用 data_only=False 调用 openpyxl 进行编辑,然后保存电子表格。
  2. 使用 subprocess.Popen 打开新的电子表格,并让 Excel 评估电子表格公式。
  3. 使用 pynput.keyboard 保存更新的电子表格并退出 Excel。
  4. 使用 data_only=True 的 openpyxl 打开更新的电子表格并获取公式的值。

下面是一个 Windows 上的测试程序,它创建一个新的工作簿,在单元格 E2 中放置 "=SUM(A1:C3)" 公式,在单元格 A1-C3 中放入数据,并计算公式的值。

from openpyxl import load_workbook, Workbook
from pynput.keyboard import Key, Controller
import subprocess
import time
import os

excel_prog = r'C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE'

# Create test Excel workbook, get default worksheet.
wb = Workbook()
ws = wb.active

# Put data and a formula into worksheet.
for row_index in range(1,4):
    for column_index in range(1,4):
        ws.cell(row = row_index, column = column_index).value = row_index + column_index
ws['E1'].value = 'Sum of cells in range A1:C3:'
ws['E2'].value = '=SUM(A1:C3)'

# Try to get value of formula.  We'll see the formula instead.
print('E2:', ws['E2'].value)

# Save and close workbook.
wb.save(filename = 'test.xlsx')
wb.close()

# Pause to give workbook time to close.
time.sleep(5)

# Open the workbook in Excel.  I specify folder, otherwise Excel will
# open in "Protected View", interfering with using pynput.
subprocess.Popen([excel_prog, os.path.join(os.getcwd(), 'test.xlsx')])

# Pause to give workbook time to open and for formulas to update.
time.sleep(5)

# Save workbook using pynput.
keyboard = Controller()
with keyboard.pressed(Key.ctrl):
    keyboard.press('s')
    keyboard.release('s')

# Pause to give workbook time to save.
time.sleep(5)

# Close workbook.
with keyboard.pressed(Key.alt):
    keyboard.press(Key.f4)
    keyboard.release(Key.f4)

# Pause to give workbook time to fully close.
time.sleep(5)

# Open Excel workbook and worksheet in openpyxl, data-only.
wb = load_workbook(filename = 'test.xlsx', data_only = True)
ws = wb.active

# Get value of the cell containing the formula.
print('E2:', ws['E2'].value)

# Close workbook.
wb.close()

-1

Xlcalculator 能够评估单元格。

from xlcalculator import ModelCompiler
from xlcalculator import Model
from xlcalculator import Evaluator

filename = r'xxxx.xlsm'
compiler = ModelCompiler()
new_model = compiler.read_and_parse_archive(filename)
evaluator = Evaluator(new_model)
val1 = evaluator.evaluate('First!A2')
print("value 'evaluated' for First!A2:", val1)

输出结果为:

First!A2的值为0.1


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接