How to use Python to read Excel files that contain extended fonts? (openpyxl error: Max value is 14)
How to use Python to read Excel files that contain extended fonts? (openpyxl error: Max value is 14)
作为 Python 的学习项目,我正在尝试读取目录中的所有 Excel 文件并提取所有工作表的名称。
我一直在尝试几个可用的 Python 模块来执行此操作(在此示例中为 pandas
),但是 运行 遇到了一个问题,其中大多数取决于 [=12] =].
这是我当前的代码:
import os
import pandas
directory_root = 'D:\testFiles'
# Dict to hold all files, stats
all_files = {}
for _current_path, _dirs_in_path, _files_in_path in os.walk(directory_root):
# Add all files to this `all_files`
for _file in _files_in_path:
# Extract filesystem stats from the file
_stats = os.stat(os.path.join(_current_path, _file))
# Add the full file path and its stats to the `all_files` dict.
all_files[os.path.join(_current_path, _file)] = _stats
# Loop through all found files to extract the sheet names
for _file in all_files:
# Open the workbook
xls = pandas.ExcelFile(_file)
# Loop through all sheets in the workbook
for _sheet in xls.sheet_names():
print(_sheet)
这会在调用 pandas.ExcelFile()
时从 openpyxl
引发错误:ValueError: Max value is 14
。
根据我在网上找到的信息,这是因为该文件包含 14 以上的字体系列。如何在忽略任何现有格式的情况下读取 Excel (xlsx) 文件?
我能找到的 only potential solution 建议修改原始文件并删除格式,但这不是一个选项,因为我不想以任何方式修改文件。
是否有另一种没有此格式限制的方法?
这很可能不是因为字体大小或字体系列,因为它给出了 ValueError。我从 this page and this page 看到的,似乎 excel 文件中的浮点值之一不能超过 14。这就是它给出错误 ValueError: Max value is 14
的原因。您可以深入文件并搜索大于 14 的值,然后通过操纵该值来尝试您的代码。
问题是您的文件不符合 Open Office 规范。只允许使用某些字体系列。一旦 openpyxl
遇到超出规格的字体,它就会抛出此错误,因为 OpenPyxl only allows spec-conforming excel files.
一些 Excel 读者可能对此没有问题,并且更灵活地处理不符合 OpenOffice 规范的文件,但 openpyxl 仅实现 Apache Open Office 规范。
正在解析的 xml 将包含有关字体的信息,如下所示:
<font>
<b/>
<sz val="11"/>
<color rgb="FF000000"/>
<name val="Century Gothic"/>
<family val="34"/>
</font>
如果 family 值超过 14,openpyxl 将抛出此 ValueError
。 Open Office 中有一个底层描述符来控制它。
当其他阅读器,例如 Microsoft Office 365 Excel 遇到此问题时,它会在加载文件时更改字体系列以兼容字体(默认字体 Calibri)。
作为解决方法,如果您不想更改该值(如 Microsoft Excel 那样),您可以对描述符进行猴子修补以允许更大的最大字体系列。
# IMPORTANT, you must do this before importing openpyxl
from unittest import mock
# Set max font family value to 100
p = mock.patch('openpyxl.styles.fonts.Font.family.max', new=100)
p.start()
import openpyxl
openpyxl.open('my-bugged-worksheet.xlsx') # this works now!
这可以使用 this excel workbook 进行复制。在补丁之前,这将无法加载。补丁后加载无误
以下是为我修复此错误的方法。我编辑了 lib\site-packages\openpyxl\descriptors\base.py
并在 class Max 中的 86
行之后添加了打印语句,如下所示:
def __set__(self, instance, value):
if ((self.allow_none and value is not None)
or not self.allow_none):
value = _convert(self.expected_type, value)
if value > self.max:
print(f"value is {value}")
raise ValueError('Max value is {0}'.format(self.max))
super(Max, self).__set__(instance, value)
这样打印出来的34
的值明显高于最大值14。
我所做的只是注释掉 raise
错误的行。
将代码更改为:
def __set__(self, instance, value):
if ((self.allow_none and value is not None)
or not self.allow_none):
value = _convert(self.expected_type, value)
if value > self.max:
self.max = value
# print(f"value is {value}")
# raise ValueError('Max value is {0}'.format(self.max))
super(Max, self).__set__(instance, value)
这解决了我的问题。
或者,如果您需要分发文件并且必须使用原始库代码 THEN,请尝试 .
# IMPORTANT, you must do this before importing openpyxl
from unittest import mock
# Set max font family value to 100
p = mock.patch('openpyxl.styles.fonts.Font.family.max', new=100)
p.start()
import openpyxl
openpyxl.open('my-bugged-worksheet.xlsx') # this works now!
在导入 openpyxl 之前。
如果我没看错,您想从目录中的文件中获取所有 xlsx sheet 名称,这样您就可以这样做:
import pandas as pd
import os
dirpth = './Target Folder/'
for dirpath, dirnames, filenames in os.walk(dirpth):
file_names = filenames
file_names = [dirpth+file_names[i] for i in range(len(file_names))]
data = []
sheet_names = []
for names in file_names:
df = pd.ExcelFile(names,engine = 'openpyxl')
data_sheet = []
sheet_temp = []
for name in df.sheet_names:
data_sheet.append(df.parse(nama,index_col = [0]))
sheet_temp.append(name)
data.append(data_sheet)
sheet_names.append(sheet_temp)
这样,对于每个 excel 文件,您将自动从每个 sheet 中获取数据,但是如果您在同一文件夹中有不同扩展名的文件(例如在与 .csv 文件相同的文件夹)。所以你需要先过滤所有文件名或者你可以使用 try except
语句跳过非 excel 文件。
如果您的 .py 文件与您的文件夹目标路径不同,只需更改 dirpath,例如:'D:/changeYour Folder Path/Example/Target/'
注意:您需要安装openpyxl
通过简单的 unzip|find 在 windows 中或在其他人中使用 grep,很容易检测到 family 值何时超出范围。因此,您可以根据这些值过滤掉文件。在这里,我们在坏男孩示例中看到它们是可接受的 2 和不可接受的 34
然而,由于所有平台(包括 win 10)都有 TAR,最简单的方法是首先将 file.xlsx 作为一个集合展开,然后在本机 OS 中使用按文件查找(或python) 然后确保您确切知道哪个文件需要调整。
所以我们现在知道它是 styles.xml(这并不奇怪,因为字体值应该在那里)
此时我们可以使用字符串替换将该条目更改为 say
<family val="3"/>
如果这对您的目的更有用。
然后重新打包调整后的 xlsx(注意:- 最好只使用一种工具来“更新”一个 style.xls 文件以保持 zip 的相对顺序)并且它的行为应该与standard.xlsx有标准的1-14字体,假设作者没有引入其他错误。
稍后编辑
我不会声称要重新发明一个 Pythonic Wheel,而只是说 (来自一个现已离职的用户)这应该适用于许多其他感兴趣的用户。备份您的文件并进行相应修改。
import tempfile
from openpyxl import load_workbook
import os
import shutil
from lxml import etree
EXCELFILE = '~/Book1.xlsx'
STYLES = 'xl/styles.xml'
FORMAT = 'zip'
with tempfile.TemporaryDirectory() as tdir:
os.chdir(tdir)
shutil.unpack_archive(filename=EXCELFILE, format=FORMAT)
with open(STYLES, 'r') as styles:
tree = etree.parse(styles)
for family in tree.xpath('//*[local-name()="fonts"]//*[local-name()="font"]//*[local-name()="family"]'):
try:
if int(family.attrib['val']) > 14:
family.set('val', '2')
except Exception:
pass
with open(STYLES, 'wb') as styles:
tree.write(styles)
shutil.make_archive(base_name=EXCELFILE, format=FORMAT)
shutil.move(f'{EXCELFILE}.{FORMAT}', EXCELFILE)
load_workbook(EXCELFILE)
调用 load_workbook() 只是为了检查修改后的电子表格的有效性
这个问题可以通过 完全清理 xlsx 样式 来解决,这是我的代码如何使用 pandas
虽然 openpyxl
作为 Python 的学习项目,我正在尝试读取目录中的所有 Excel 文件并提取所有工作表的名称。
我一直在尝试几个可用的 Python 模块来执行此操作(在此示例中为 pandas
),但是 运行 遇到了一个问题,其中大多数取决于 [=12] =].
这是我当前的代码:
import os
import pandas
directory_root = 'D:\testFiles'
# Dict to hold all files, stats
all_files = {}
for _current_path, _dirs_in_path, _files_in_path in os.walk(directory_root):
# Add all files to this `all_files`
for _file in _files_in_path:
# Extract filesystem stats from the file
_stats = os.stat(os.path.join(_current_path, _file))
# Add the full file path and its stats to the `all_files` dict.
all_files[os.path.join(_current_path, _file)] = _stats
# Loop through all found files to extract the sheet names
for _file in all_files:
# Open the workbook
xls = pandas.ExcelFile(_file)
# Loop through all sheets in the workbook
for _sheet in xls.sheet_names():
print(_sheet)
这会在调用 pandas.ExcelFile()
时从 openpyxl
引发错误:ValueError: Max value is 14
。
根据我在网上找到的信息,这是因为该文件包含 14 以上的字体系列。如何在忽略任何现有格式的情况下读取 Excel (xlsx) 文件?
我能找到的 only potential solution 建议修改原始文件并删除格式,但这不是一个选项,因为我不想以任何方式修改文件。
是否有另一种没有此格式限制的方法?
这很可能不是因为字体大小或字体系列,因为它给出了 ValueError。我从 this page and this page 看到的,似乎 excel 文件中的浮点值之一不能超过 14。这就是它给出错误 ValueError: Max value is 14
的原因。您可以深入文件并搜索大于 14 的值,然后通过操纵该值来尝试您的代码。
问题是您的文件不符合 Open Office 规范。只允许使用某些字体系列。一旦 openpyxl
遇到超出规格的字体,它就会抛出此错误,因为 OpenPyxl only allows spec-conforming excel files.
一些 Excel 读者可能对此没有问题,并且更灵活地处理不符合 OpenOffice 规范的文件,但 openpyxl 仅实现 Apache Open Office 规范。
正在解析的 xml 将包含有关字体的信息,如下所示:
<font>
<b/>
<sz val="11"/>
<color rgb="FF000000"/>
<name val="Century Gothic"/>
<family val="34"/>
</font>
如果 family 值超过 14,openpyxl 将抛出此 ValueError
。 Open Office 中有一个底层描述符来控制它。
当其他阅读器,例如 Microsoft Office 365 Excel 遇到此问题时,它会在加载文件时更改字体系列以兼容字体(默认字体 Calibri)。
作为解决方法,如果您不想更改该值(如 Microsoft Excel 那样),您可以对描述符进行猴子修补以允许更大的最大字体系列。
# IMPORTANT, you must do this before importing openpyxl
from unittest import mock
# Set max font family value to 100
p = mock.patch('openpyxl.styles.fonts.Font.family.max', new=100)
p.start()
import openpyxl
openpyxl.open('my-bugged-worksheet.xlsx') # this works now!
这可以使用 this excel workbook 进行复制。在补丁之前,这将无法加载。补丁后加载无误
以下是为我修复此错误的方法。我编辑了 lib\site-packages\openpyxl\descriptors\base.py
并在 class Max 中的 86
行之后添加了打印语句,如下所示:
def __set__(self, instance, value):
if ((self.allow_none and value is not None)
or not self.allow_none):
value = _convert(self.expected_type, value)
if value > self.max:
print(f"value is {value}")
raise ValueError('Max value is {0}'.format(self.max))
super(Max, self).__set__(instance, value)
这样打印出来的34
的值明显高于最大值14。
我所做的只是注释掉 raise
错误的行。
将代码更改为:
def __set__(self, instance, value):
if ((self.allow_none and value is not None)
or not self.allow_none):
value = _convert(self.expected_type, value)
if value > self.max:
self.max = value
# print(f"value is {value}")
# raise ValueError('Max value is {0}'.format(self.max))
super(Max, self).__set__(instance, value)
这解决了我的问题。
或者,如果您需要分发文件并且必须使用原始库代码 THEN,请尝试
# IMPORTANT, you must do this before importing openpyxl
from unittest import mock
# Set max font family value to 100
p = mock.patch('openpyxl.styles.fonts.Font.family.max', new=100)
p.start()
import openpyxl
openpyxl.open('my-bugged-worksheet.xlsx') # this works now!
在导入 openpyxl 之前。
如果我没看错,您想从目录中的文件中获取所有 xlsx sheet 名称,这样您就可以这样做:
import pandas as pd
import os
dirpth = './Target Folder/'
for dirpath, dirnames, filenames in os.walk(dirpth):
file_names = filenames
file_names = [dirpth+file_names[i] for i in range(len(file_names))]
data = []
sheet_names = []
for names in file_names:
df = pd.ExcelFile(names,engine = 'openpyxl')
data_sheet = []
sheet_temp = []
for name in df.sheet_names:
data_sheet.append(df.parse(nama,index_col = [0]))
sheet_temp.append(name)
data.append(data_sheet)
sheet_names.append(sheet_temp)
这样,对于每个 excel 文件,您将自动从每个 sheet 中获取数据,但是如果您在同一文件夹中有不同扩展名的文件(例如在与 .csv 文件相同的文件夹)。所以你需要先过滤所有文件名或者你可以使用 try except
语句跳过非 excel 文件。
如果您的 .py 文件与您的文件夹目标路径不同,只需更改 dirpath,例如:'D:/changeYour Folder Path/Example/Target/'
注意:您需要安装openpyxl
通过简单的 unzip|find 在 windows 中或在其他人中使用 grep,很容易检测到 family 值何时超出范围。因此,您可以根据这些值过滤掉文件。在这里,我们在坏男孩示例中看到它们是可接受的 2 和不可接受的 34
然而,由于所有平台(包括 win 10)都有 TAR,最简单的方法是首先将 file.xlsx 作为一个集合展开,然后在本机 OS 中使用按文件查找(或python) 然后确保您确切知道哪个文件需要调整。
所以我们现在知道它是 styles.xml(这并不奇怪,因为字体值应该在那里)
此时我们可以使用字符串替换将该条目更改为 say
<family val="3"/>
如果这对您的目的更有用。
然后重新打包调整后的 xlsx(注意:- 最好只使用一种工具来“更新”一个 style.xls 文件以保持 zip 的相对顺序)并且它的行为应该与standard.xlsx有标准的1-14字体,假设作者没有引入其他错误。
稍后编辑 我不会声称要重新发明一个 Pythonic Wheel,而只是说 (来自一个现已离职的用户)这应该适用于许多其他感兴趣的用户。备份您的文件并进行相应修改。
import tempfile
from openpyxl import load_workbook
import os
import shutil
from lxml import etree
EXCELFILE = '~/Book1.xlsx'
STYLES = 'xl/styles.xml'
FORMAT = 'zip'
with tempfile.TemporaryDirectory() as tdir:
os.chdir(tdir)
shutil.unpack_archive(filename=EXCELFILE, format=FORMAT)
with open(STYLES, 'r') as styles:
tree = etree.parse(styles)
for family in tree.xpath('//*[local-name()="fonts"]//*[local-name()="font"]//*[local-name()="family"]'):
try:
if int(family.attrib['val']) > 14:
family.set('val', '2')
except Exception:
pass
with open(STYLES, 'wb') as styles:
tree.write(styles)
shutil.make_archive(base_name=EXCELFILE, format=FORMAT)
shutil.move(f'{EXCELFILE}.{FORMAT}', EXCELFILE)
load_workbook(EXCELFILE)
调用 load_workbook() 只是为了检查修改后的电子表格的有效性
这个问题可以通过 完全清理 xlsx 样式 来解决,这是我的代码如何使用 pandas
虽然 openpyxl