【问题标题】:How to obtain sheet names from XLS files without loading the whole file?如何在不加载整个文件的情况下从 XLS 文件中获取工作表名称?
【发布时间】:2012-08-28 07:38:50
【问题描述】:

我目前正在使用 pandas 读取 Excel 文件并将其工作表名称呈现给用户,以便他可以选择他想使用的工作表。问题是文件非常大(70 列 x 65k 行),在笔记本上加载最多需要 14 秒(CSV 文件中的相同数据需要 3 秒)。

我在 panda 中的代码是这样的:

xls = pandas.ExcelFile(path)
sheets = xls.sheet_names

我之前尝试过 xlrd,但得到了类似的结果。这是我的 xlrd 代码:

xls = xlrd.open_workbook(path)
sheets = xls.sheet_names

那么,谁能提出一种比读取整个文件更快的方法来从 Excel 文件中检索工作表名称?

【问题讨论】:

  • 在Excel中直接打开这样的文件需要多长时间?
  • @DocBrown 如果 Excel 已经打开,大约需要 4 秒(Excel Starter 2010)

标签: python excel pandas xlrd


【解决方案1】:

您可以使用xlrd 库并使用“on_demand=True”标志打开工作簿,这样就不会自动加载工作表。

您可以通过类似于 pandas 的方式检索工作表名称:

import xlrd
xls = xlrd.open_workbook(r'<path_to_your_excel_file>', on_demand=True)
print xls.sheet_names() # <- remeber: xlrd sheet_names is a function, not a property

【讨论】:

  • xlrd 文档说这仅适用于 BIFF >= 5.0。它对我不起作用,如果我问 xls.biff_version 它说“0”,这看起来很奇怪(不可能?)。知道会发生什么吗?
  • 您也可以将结果从xlrd.open_workbook 传递给pandas.ExcelFile 并像以前一样与对象交互:xls = pandas.ExcelFile(xlrd.open_workbook(path, on_demand=True))
  • 这对于工作表名称来说太慢了。
  • 5MB 文件花费了 11 秒。似乎它加载了整个文件
  • @rluts 我同意,这种方法仍然很慢。有更好的解决方案吗?
【解决方案2】:

我已经尝试过 xlrd、pandas、openpyxl 和其他此类库,随着文件大小在读取整个文件时增加,它们似乎都需要指数级时间。上面提到的其他使用“on_demand”的解决方案对我不起作用。以下函数适用于 xlsx 文件。

def get_sheet_details(file_path):
    sheets = []
    file_name = os.path.splitext(os.path.split(file_path)[-1])[0]
    # Make a temporary directory with the file name
    directory_to_extract_to = os.path.join(settings.MEDIA_ROOT, file_name)
    os.mkdir(directory_to_extract_to)

    # Extract the xlsx file as it is just a zip file
    zip_ref = zipfile.ZipFile(file_path, 'r')
    zip_ref.extractall(directory_to_extract_to)
    zip_ref.close()

    # Open the workbook.xml which is very light and only has meta data, get sheets from it
    path_to_workbook = os.path.join(directory_to_extract_to, 'xl', 'workbook.xml')
    with open(path_to_workbook, 'r') as f:
        xml = f.read()
        dictionary = xmltodict.parse(xml)
        for sheet in dictionary['workbook']['sheets']['sheet']:
            sheet_details = {
                'id': sheet['sheetId'], # can be @sheetId for some versions
                'name': sheet['name'] # can be @name
            }
            sheets.append(sheet_details)

    # Delete the extracted files directory
    shutil.rmtree(directory_to_extract_to)
    return sheets

由于所有 xlsx 基本上都是压缩文件,我们提取底层 xml 数据并直接从工作簿中读取工作表名称,与库函数相比,这需要几分之一秒。

基准测试:(在 4 张 6mb xlsx 文件上)
熊猫,xlrd: 12 秒
openpyxl: 24 秒
建议方法: 0.4秒

【讨论】:

  • 您需要配置一堆 Django 设置才能使用它。你能告诉我怎么做吗?
  • 绕过 Django 要求,我也遇到了这个错误:File is not a zip file 我的文件是 Microsoft Excel 97-2003 工作表 (.xls) 也许它不适用于旧版本的 Excel
  • @CoreyLevinson,如帖子中所述,这仅适用于 xlsx 文件,因为 .xlsx 文件使用 Office Open XML,这是一种压缩的 XML 格式。 .xls 文件使用 BIFF 格式。我还没有遇到对 xls 文件的要求。但是,如果您能找到解压缩和阅读内部内容的解决方法,我认为这种方法也适用于您。此外,无需更改 django 设置我只使用 media_cdn 或 BASE_DIR 并在相对于它的某处创建一个临时目录。
  • 您的代码节省了数小时的工作量,这些代码有一个我必须修复的问题,基本上,如果 Excel 只有一张纸,它会抛出一个错误,我使用这些单行代码修复了 sheets1 = dictionary['workbook']['sheets']['sheet'] if not(isinstance(sheets1, list)): sheets1 =[sheets1]
  • @piyushmandovra 我做了同样的修复并回到这里添加评论只是为了在这里找到你的评论哈哈。
【解决方案3】:

根据我对标准/流行库的研究,截至 2020 尚未为 xlsx / xls 实现此功能,但您可以为 xlsb 执行此操作。无论哪种方式,这些解决方案都应该为您带来巨大的性能改进。对于xlsxlsxxlsb

以下是在 ~10Mb xlsxxlsb 文件上进行的基准测试。

xlsx, xls

from openpyxl import load_workbook

def get_sheetnames_xlsx(filepath):
    wb = load_workbook(filepath, read_only=True, keep_links=False)
    return wb.sheetnames

基准测试: ~ 14 倍速度提升

# get_sheetnames_xlsx vs pd.read_excel
225 ms ± 6.21 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
3.25 s ± 140 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

xlsb

from pyxlsb import open_workbook

def get_sheetnames_xlsb(filepath):
  with open_workbook(filepath) as wb:
     return wb.sheets

基准测试: ~ 速度提升 56 倍

# get_sheetnames_xlsb vs pd.read_excel
96.4 ms ± 1.61 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
5.36 s ± 162 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

注意事项:

【讨论】:

  • 优秀的答案格伦!谢谢
【解决方案4】:

通过将@Dhwanil shah 的答案与here 的答案结合起来,我编写的代码也与只有一张纸的 xlsx 文件兼容:

def get_sheet_ids(file_path):
sheet_names = []
with zipfile.ZipFile(file_path, 'r') as zip_ref:
    xml = zip_ref.open(r'xl/workbook.xml').read()
    dictionary = xmltodict.parse(xml)

    if not isinstance(dictionary['workbook']['sheets']['sheet'], list):
        sheet_names.append(dictionary['workbook']['sheets']['sheet']['@name'])
    else:
        for sheet in dictionary['workbook']['sheets']['sheet']:
            sheet_names.append(sheet['@name'])
return sheet_names

【讨论】:

    【解决方案5】:

    Python 代码适配,传递了完整的 pathlib 路径文件名(例如,('c:\xml\file.xlsx'))。 来自Dhwanil shah 的回答,没有用于创建临时目录的 Django 方法。

    import xmltodict
    import shutil
    import zipfile
    
    
    def get_sheet_details(filename):
        sheets = []
        # Make a temporary directory with the file name
        directory_to_extract_to = (filename.with_suffix(''))
        directory_to_extract_to.mkdir(parents=True, exist_ok=True)
        # Extract the xlsx file as it is just a zip file
        zip_ref = zipfile.ZipFile(filename, 'r')
        zip_ref.extractall(directory_to_extract_to)
        zip_ref.close()
        # Open the workbook.xml which is very light and only has meta data, get sheets from it
        path_to_workbook = directory_to_extract_to / 'xl' / 'workbook.xml'
        with open(path_to_workbook, 'r') as f:
            xml = f.read()
            dictionary = xmltodict.parse(xml)
            for sheet in dictionary['workbook']['sheets']['sheet']:
                sheet_details = {
                    'id': sheet['@sheetId'],  # can be sheetId for some versions
                    'name': sheet['@name']  # can be name
                }
                sheets.append(sheet_details)
        # Delete the extracted files directory
        shutil.rmtree(directory_to_extract_to)
        return sheets
    

    【讨论】:

      【解决方案6】:

      你也可以使用

      data=pd.read_excel('demanddata.xlsx',sheet_name='oil&gas')
      print(data)   
      

      这里的 demanddata 是你的文件名 石油和天然气是您的工作表名称之一。让您的工作表中可能有 n 个工作表。只需在 Sheet_name="Name of Your required sheet" 处提供您要获取的工作表的名称

      【讨论】:

      • 我建议解释为什么这种方法比他目前的方法更快,或者表明它在某些情况下更快。 (总体上提供更多证据)这可能是一个很好的答案,但提供更多关于为什么它很棒的基础将有助于读者评估它。
      • 谢谢@Xgongiveittoya。这是个好建议,我会做的。
      • 这不符合 OP 的要求,即从工作簿中获取工作表的名称(最初未知)。相反,这会从已知的工作表中提取数据。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-28
      • 2020-02-08
      • 1970-01-01
      • 1970-01-01
      • 2014-08-10
      • 2021-11-04
      • 1970-01-01
      相关资源
      最近更新 更多