web-dev-qa-db-fra.com

Lire la valeur de la cellule Excel et non la formule qui la calcule -openpyxl

J'utilise openpyxl pour lire la valeur d'une cellule (addin-webservice Excel met à jour cette colonne.)

J'ai utilisé data_only = True mais il n’affiche pas la valeur de la cellule actuelle mais la valeur enregistrée la dernière fois que Excel a lu la feuille.

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

Comment puis-je lire la valeur réelle de la cellule?

34
user3411047
wb = openpyxl.load_workbook(filename, data_only=True)

Le data_only drapeau aide.

96
Marcin Kajzler

Comme @ alex-martelli le dit, openpyxl n'évalue pas les formules. Lorsque vous ouvrez un fichier Excel avec openpyxl, vous avez le choix entre lire les formules ou la dernière valeur calculée. Si, comme vous l'avez indiqué, la formule dépend de compléments, la valeur mise en cache ne peut jamais être précise. En tant que compléments en dehors de la spécification de fichier, ils ne seront jamais pris en charge. Au lieu de cela, vous voudrez peut-être regarder quelque chose comme xlwings qui peut interagir avec le runtime Excel.

10
Charlie Clark

Face au même problème. Nécessaire pour lire les valeurs de cellule quelles que soient ces cellules: scalaires, formules avec des valeurs précalculées ou formules sans eux, avec une tolérance d'échec préférée à la correction.

La stratégie est assez simple:

  1. si une cellule ne contient pas de formule, retourne la valeur de la cellule;
  2. si c'est une formule, essayez d'obtenir sa valeur précalculée;
  3. sinon, essayez de l'évaluer avec pycel ;
  4. en cas d'échec (en raison de la prise en charge limitée des formules par pycel ou en cas d'erreur), avertir et renvoyer Aucune.

J'ai créé une classe qui cache toutes ces machines et fournit une interface simple pour lire les valeurs de cellules.

Il est facile de modifier la classe afin qu'elle lève une exception à l'étape 4, si la correction est préférée à la tolérance de panne.

J'espère que ça va aider quelqu'un.

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}")
3
krvkir

Comme @Charlie Clark l'a mentionné, vous pouvez utiliser xlwings (si vous avez MS Excel). Voici un exemple

disons que vous avez une feuille de calcul Excel avec des formules, pour l'exemple, j'en définit une avec 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')

Comme mentionné, si nous chargeons à nouveau Excel avec openpyxl, nous n'obtiendrons pas la formule évaluée.

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

vous pouvez utiliser xlwings pour obtenir la formule évaluée par Excel:

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

qui retourne 3, la valeur attendue.

Je l'ai trouvé très utile lorsque vous travaillez avec des feuilles de calcul avec des formules très complexes et des références entre les feuilles.

1
Nabla