处理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!')
关键修正点
- 手动定义命名空间:创建
namespaces字典,对应XML中的命名空间前缀与URI - 正确获取属性值:目标
<meta>标签的ID存储在content属性中,使用.get('content')获取而非.text - 增加异常防护:检查元素是否存在,避免
None调用属性导致崩溃 - 避免多XML干扰:找到第一个XML文件后立即退出循环
- 重名保护:检查新文件名是否已存在,防止覆盖已有文件
内容的提问来源于stack exchange,提问作者DrChimRichaulds
相关产品推荐
相关产品推荐

