You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python2.7自动检测ArcGIS/xlsxwriter/SpatiaLite生成的损坏Excel文件

自动检测异常Excel文件的方案

问题背景

在ArcGIS 10.6中通过Python工具箱,使用xlsxwriter从SpatiaLite数据库生成带数据和图片的Excel报表,运行环境为Excel 10 + Python 2.7。多数报表正常,但部分文件打开时会触发错误,提示需移除/xl/workbook.xml中的命名范围,Excel需恢复数据才能打开,修复后内容无异常。

已做排查:

  • 对比正常/异常报表数据,未发现差异;
  • 解压Excel查看XML,肉眼未发现问题;
  • 注释打印区域、分页符相关代码后,错误仍存在;
  • 基于xlrd的检测脚本(如下)显示所有文件正常,但实际部分打开报错:
def test_book(filename):
    try:
        book = open_workbook(filename)
        try:
            sheet = book.sheet_by_index(0)
            b6 = sheet.cell_value(rowx=5, colx=1) #b6 an internal code
            print "'"+ str(sheet.name) + "',"
            return True
        
        except XLRDError:
            return False
            print "error"
    except Exception as e:
        return False
        print "error"

补充异常文件的workbook.xml内容:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><fileVersion appName="xl" lastEdited="4" lowestEdited="4" rupBuild="4505"/><workbookPr defaultThemeVersion="124226"/><bookViews><workbookView xWindow="240" yWindow="15" windowWidth="16095" windowHeight="9660"/></bookViews><sheets><sheet name="R7MB22" sheetId="1" r:id="rId1"/></sheets><definedNames><definedName name="_xlnm.Print_Area" localSheetId="0">R7MB22!$A$1:$H$137</definedName><definedName name="_xlnm.Print_Titles" localSheetId="0">R7MB22!$1:$2</definedName></definedNames><calcPr calcId="124519" fullCalcOnLoad="1"/></workbook>

核心需求:批量检测300+周期性生成的报表,自动识别打开会报错的文件。


问题根源

从报错提示和异常XML来看,问题出在命名范围的localSheetId与工作表sheetId不匹配:

  • 异常文件中,命名范围的localSheetId="0",但工作表的sheetId="1",Excel 10对这种ID映射不兼容的情况判定为文件损坏;
  • xlrd仅校验文件是否可读取,不会检查XML结构的逻辑合法性,因此无法检测出这类问题。

自动检测方案

方案1:解析Excel的XML结构校验

Excel本质是ZIP压缩包,直接读取xl/workbook.xml,校验命名范围的localSheetId是否存在对应的工作表sheetId:

import zipfile
import xml.etree.ElementTree as ET

def check_excel_named_ranges(filename):
    # 定义XML命名空间
    ns = {'ss': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'}
    try:
        with zipfile.ZipFile(filename, 'r') as zip_file:
            # 读取workbook.xml文件
            with zip_file.open('xl/workbook.xml') as xml_file:
                tree = ET.parse(xml_file)
                root = tree.getroot()
                
                # 收集所有工作表的sheetId
                sheet_ids = []
                sheets = root.findall('.//ss:sheet', ns)
                for sheet in sheets:
                    sheet_id = sheet.get('sheetId')
                    sheet_ids.append(sheet_id)
                
                # 检查每个命名范围的localSheetId是否有效
                defined_names = root.findall('.//ss:definedName', ns)
                for dn in defined_names:
                    local_sheet_id = dn.get('localSheetId')
                    if local_sheet_id and local_sheet_id not in sheet_ids:
                        print(f"[异常] {filename}:命名范围localSheetId={local_sheet_id}无对应工作表")
                        return False
        return True
    except Exception as e:
        print(f"[错误] {filename}:读取失败 - {str(e)}")
        return False

使用时遍历所有Excel文件调用该函数,返回False的即为异常文件。该方法速度快,无需依赖Excel环境。

方案2:利用Excel COM组件检测(Windows环境适用)

通过Excel自身的COM接口打开文件,模拟实际用户打开场景,捕获是否需要修复文件,准确性最高:

import win32com.client
import os

def check_excel_with_com(filename):
    excel_app = None
    try:
        # 初始化Excel应用
        excel_app = win32com.client.Dispatch("Excel.Application")
        excel_app.Visible = False
        excel_app.DisplayAlerts = False
        
        # 以修复模式打开文件
        workbook = excel_app.Workbooks.Open(filename, CorruptLoad=1)
        
        # 检查是否进入修复模式
        if workbook.RepairMode:
            print(f"[异常] {filename}:Excel检测到损坏并修复")
            result = False
        else:
            result = True
        
        workbook.Close(SaveChanges=False)
        return result
    except Exception as e:
        print(f"[错误] {filename}:打开失败 - {str(e)}")
        return False
    finally:
        # 确保退出Excel进程
        if excel_app:
            excel_app.Quit()

注意:需要安装pywin32库(执行pip install pywin32),批量检测时速度较慢,但结果最贴近实际打开情况。


额外修复建议

若要从根源解决问题,修改xlsxwriter生成报表的代码:

  • 确保设置打印区域/标题时,localSheetId与工作表的sheetId一致;
  • 直接通过xlsxwriter的工作表对象设置打印区域(如worksheet.set_print_area('A1:H137')),库会自动处理ID映射,避免手动设置导致的不匹配。

内容的提问来源于stack exchange,提问作者nanunga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 08:35:03