读取 Excel 单元格值而不是计算它的公式 -openpyxl

Posted

技术标签:

【中文标题】读取 Excel 单元格值而不是计算它的公式 -openpyxl【英文标题】:Read Excel cell value and not the formula computing it -openpyxl 【发布时间】:2015-04-15 13:14:59 【问题描述】:

我正在使用 openpyxl 读取单元格值(excel addin-webservice 更新此列。 )

我使用了data_only = True,但它没有显示当前单元格的值,而是 Excel 上次读取工作表时存储的值。

wbFile = openpyxl.load_workbook(filename = xxxx,data_only=True)
wsFile = wbFile[c_sSheet]

如何读取单元格实际值?

【问题讨论】:

我不认为openpyxl 复制了 Excel 庞大的公式评估子系统,所以我认为你不能让它自动重新计算所有公式。最好的情况是,您可以为该大型公式语言的子集实现自己的基于 eval 的解释器。 我不需要计算或执行论坛。我只需要读取单元格值。 “单元格值”(超出公式)是 Excel 在保存工作表时保存的值,并且您暗示这不是您真正想要的 - 您想要“当前”值(取决于否怀疑其他单元格中的值),这意味着您确实需要根据其他单元格的当前值重新计算公式! 抱歉让您感到困惑,基本上我的列是通过 excel 插件更新的(webservice 将轮询数据)...我需要提取该列单元格值。 我认为您需要 (A) 再次使用 Excel 来重新计算公式,或者通过上述插件或 xlwings.org 等;或(B)如果公式足够简单,如我所说,实现您自己的公式解释器。 openpyxl 和其他不使用 Excel 的 Excel 文件阅读器不会进行您需要的公式评估(无论您认为是否需要:-)。 【参考方案1】:

正如@alex-martelli 所说,openpyxl 不计算公式。当您使用 openpyxl 打开 Excel 文件时,您可以选择读取公式或最后计算的值。如果如您所指,公式依赖于加载项,则缓存值永远不会准确。作为文件规范之外的加载项,它们将永远不受支持。相反,您可能想查看类似 xlwings 的东西,它可以与 Excel 运行时交互。

【讨论】:

关于这个答案的两个问题:(1)你如何区分打开 XL 文件来阅读论坛和论坛。读取最后的计算值?这是通过data_only=True 参数吗? (2)last calculated value在现实世界中是什么意思?也就是说,如果 XL 文件在最后一次(手动/人工)更改后在退出时保存,这是否意味着所有单元格都被重新计算/在它们的“最后计算值”?通常何时重新计算单元格值? (我意识到这比 OpenPyXL 更像是一个 Excel 问题,但希望能得到澄清) 没关系,我想我在这里找到了答案:***.com/questions/36116162/…【参考方案2】:
wb = openpyxl.load_workbook(filename, data_only=True)

data_only 标志有帮助。

【讨论】:

是的。这回答了这个问题。如果您想同时访问方程式和值,那么您可能会遇到两种情况,比如wb2 = openpyxl.load_workbook(filename)。然后在读取相应的索引时,您会从wb 获得值,并从wb2 获得方程。 嗨,单元格不是空的。这是因为 openpyxl 不计算公式。我通过获取 excel 数据并将所有计算放在服务器上来解决这个问题:| 可能值得在bitbucket.org/openpyxl/openpyxl/issues 的官方功能跟踪器中添加一个问题,但从bitbucket.org/openpyxl/openpyxl/issues/291/… 判断,我怀疑这是一个故意避免的功能:对公式的编辑将难以同步计算的值。您必须重新实现整个公式评估系统。这可能是侵犯版权,需要 Excel(或 OpenOffice)许可证,或将工作簿限制为有效只读。 它没有回答最初的问题?在这种情况下使用 xlwings 包是一种解决方案。 如果 data_only = True,cell.value 将返回该值(正如 Excel 在写入文件时最后知道的那样)。 cell.internal_value 将返回公式。【参考方案3】:

面临同样的问题。无论这些单元格是什么,都需要读取单元格值:标量、具有预先计算值的公式或没有它们的公式,容错性优于正确性。

策略非常简单:

    如果单元格不包含公式,则返回单元格的值; 如果是公式,请尝试获取其预先计算的值; 如果不能,请尝试使用pycel 评估它; 如果失败(由于pycel 对公式的支持有限或有一些错误),发出警告并返回无。

我创建了一个隐藏所有这些机制的类,并为读取单元格值提供了简单的接口。

如果优先考虑正确性而不是容错性,则可以轻松修改类,以便在第 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 应该知道pycel 只支持有限的一组 Excel 函数。但对于简单的情况,它工作正常。 这有几个问题:TYPE_FORMULA 应该来自from openpyxl.cell.cell import TYPE_FORMULA,gen_graph() 不再可用 - 直接使用 evaluate()。【参考方案4】:

正如@Charlie Clark 提到的,您可以使用xlwings(如果您有 MS Excel)。这里是一个例子

假设您有一个带有公式的 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,即预期值。

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

【讨论】:

看起来只能在 Windows 上工作。 xlwings 无法安装在 Linux 上。 确实,这些公式是由 MS excel 评估的。我认为这个解决方案只能在使用这个软件的机器上工作(虽然这个库应该在 mac 上工作)【参考方案5】:

我通过以下方式解决了这个问题:

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)

希望对你有帮助...

【讨论】:

【参考方案6】:

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

【讨论】:

【参考方案7】:

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

【讨论】:

【参考方案8】:

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)

【讨论】:

【参考方案9】:

我没有使用 Python 库来进行 Excel 计算,而是使用 Excel 来进行计算。

为什么?它不是纯 Python,但它最大限度地减少了涉及的 Python 数量。我没有使用 Python 来评估 Excel 公式,而是让 Excel 处理它自己的功能。这避免了评估 Excel 公式的 Python 中任何可能的错误。 下面概述了这种方法的工作原理:

    使用 data_only=False 调用 openpyxl 以编辑并保存电子表格。 使用 subprocess.Popen 在 Excel 中打开新电子表格,并让 Excel 计算电子表格公式。 使用 pynput.keyboard 保存更新的电子表格并退出 Excel。 使用带有 data_only=True 的 openpyxl 打开更新的电子表格并获取公式的值。

这是一个适用于 Windows 的测试程序,它创建一个新工作簿,将公式“=SUM(Al:C3)”放入单元格 E2,将数据放入单元格 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()

【讨论】:

以上是关于读取 Excel 单元格值而不是计算它的公式 -openpyxl的主要内容,如果未能解决你的问题,请参考以下文章

如何使用apache poi从公式单元格中读取单元格值

java导出excel,单元格的格式为下拉框。打开excel时,提示发现不可读取内容和已修复了公式和单元格值

使用公式或函数比较单元格值并在 Excel 中打印计数?

EXCEL中如何引用用公式算出的数值

读取 .xlsx 并访问单元格值,但不是通过它们的位置

vc++ 读取excel多个单元格的问题