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

处理DAISY ZIP文件遇AttributeError:Element无nsmap属性

解决DAISY ZIP文件重命名时的XML命名空间错误

问题背景

需要批量处理文件夹中的DAISY格式ZIP文件,完成以下操作:

  • 打开每个ZIP压缩包
  • 读取包内唯一的XML文件
  • 从<meta name="dc:Identifier" content="12345-eng-epb"/>这类元素中提取纯数字部分
  • 将ZIP文件重命名为提取出的数字(如12345.zip)

用户编写的代码运行时抛出AttributeError: 'xml.etree.ElementTree.Element' object has no attribute 'nsmap'错误,原代码如下:

import os
import zipfile
import xml.etree.ElementTree as ET

# Enter the path to the folder containing the zip files
folder_path = r'C:\Users\XXXX\XXXXXXXXX\XXXXXX_Work\bookshare_DAISY\rename_test_XXXXX'

# Loop through each file in the folder
for file_name in os.listdir(folder_path):
    if file_name.endswith('.zip'):
        # Get the full path to the zip file
        zip_file_path = os.path.join(folder_path, file_name)

        # Extract the UID from the XML file in the zip file
        with zipfile.ZipFile(zip_file_path, 'r') as zip_file:
            for inner_file_name in zip_file.namelist():
                if inner_file_name.endswith('.xml'):
                    with zip_file.open(inner_file_name) as inner_file:
                        tree = ET.parse(inner_file)
                        root = tree.getroot()
                        uid = root.find('.//dc:identifier', root.nsmap).text

        # Remove any non-digit characters from the UID
        uid_numbers = ''.join(filter(str.isdigit, uid))

        # Generate the new zip file name and path
        new_zip_file_name = f'{uid_numbers}.zip'
        new_zip_file_path = os.path.join(folder_path, new_zip_file_name)

        # Rename the zip file
        os.rename(zip_file_path, new_zip_file_path)

print('Done!')

错误原因分析

xml.etree.ElementTree.Element对象不存在nsmap属性,你试图通过root.nsmap获取命名空间映射是错误的。在ElementTree中,需要手动定义命名空间字典并在XPath查询中使用。

此外原代码还有几个潜在问题:

  • 若ZIP内有多个XML文件,会循环覆盖uid变量导致错误
  • 未处理find方法返回None的情况(找不到目标元素时会崩溃)
  • 错误地取.text属性,目标<meta>标签的ID实际在content属性中,而非文本内容

修正后的代码

import os
import zipfile
import xml.etree.ElementTree as ET

# 目标文件夹路径
folder_path = r'C:\Users\XXXX\XXXXXXXXX\XXXXXX_Work\bookshare_DAISY\rename_test_XXXXX'

# 定义DAISY XML常用的命名空间(可根据实际XML文件调整)
namespaces = {
    'dc': 'http://purl.org/dc/elements/1.1/'
}

for file_name in os.listdir(folder_path):
    if not file_name.endswith('.zip'):
        continue

    zip_file_path = os.path.join(folder_path, file_name)
    uid_numbers = None

    with zipfile.ZipFile(zip_file_path, 'r') as zip_file:
        # 遍历ZIP内的XML文件,假设仅存在一个
        for inner_file_name in zip_file.namelist():
            if inner_file_name.endswith('.xml'):
                with zip_file.open(inner_file_name) as inner_file:
                    tree = ET.parse(inner_file)
                    root = tree.getroot()

                    # 查找目标meta元素,匹配name属性并获取content值
                    meta_element = root.find('.//meta[@name="dc:Identifier"]', namespaces)
                    if meta_element is not None:
                        uid = meta_element.get('content')
                        # 提取纯数字
                        uid_numbers = ''.join(filter(str.isdigit, uid))
                    break  # 找到目标XML后退出循环

    # 确保获取到有效数字才执行重命名
    if uid_numbers:
        new_zip_file_name = f'{uid_numbers}.zip'
        new_zip_file_path = os.path.join(folder_path, new_zip_file_name)
        # 避免重名覆盖
        if not os.path.exists(new_zip_file_path):
            os.rename(zip_file_path, new_zip_file_path)
        else:
            print(f'文件 {new_zip_file_name} 已存在,跳过 {file_name}')
    else:
        print(f'未找到有效ID,跳过 {file_name}')

print('Done!')

关键修正点

  1. 手动定义命名空间:创建namespaces字典,对应XML中的命名空间前缀与URI
  2. 正确获取属性值:目标<meta>标签的ID存储在content属性中,使用.get('content')获取而非.text
  3. 增加异常防护:检查元素是否存在,避免None调用属性导致崩溃
  4. 避免多XML干扰:找到第一个XML文件后立即退出循环
  5. 重名保护:检查新文件名是否已存在,防止覆盖已有文件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 19:39:23