我如何在lxml中使用xml命名空间进行查找/查找所有?

2024-05-18 17:51:30 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试分析OpenOffice ODS电子表格中的内容。ods格式本质上只是一个包含许多文档的zipfile。电子表格的内容存储在“content.xml”中。

import zipfile
from lxml import etree

zf = zipfile.ZipFile('spreadsheet.ods')
root = etree.parse(zf.open('content.xml'))

电子表格的内容位于单元格中:

table = root.find('.//{urn:oasis:names:tc:opendocument:xmlns:table:1.0}table')

我们也可以直走:

rows = root.findall('.//{urn:oasis:names:tc:opendocument:xmlns:table:1.0}table-row')

各个元素都知道名称空间:

>>> table.nsmap['table']
'urn:oasis:names:tc:opendocument:xmlns:table:1.0'

如何在find/findall中直接使用名称空间?

显而易见的解决办法行不通。

正在尝试从表中获取行:

>>> root.findall('.//table:table')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "lxml.etree.pyx", line 1792, in lxml.etree._ElementTree.findall (src/lxml/lxml.etree.c:41770)
  File "lxml.etree.pyx", line 1297, in lxml.etree._Element.findall (src/lxml/lxml.etree.c:37027)
  File "/usr/lib/python2.6/dist-packages/lxml/_elementpath.py", line 225, in findall
    return list(iterfind(elem, path))
  File "/usr/lib/python2.6/dist-packages/lxml/_elementpath.py", line 200, in iterfind
    selector = _build_path_iterator(path)
  File "/usr/lib/python2.6/dist-packages/lxml/_elementpath.py", line 184, in _build_path_iterator
    selector.append(ops[token[0]](_next, token))
KeyError: ':'

Tags: pathin内容nameslinetablerootlxml
3条回答

如果root.nsmap包含table命名空间前缀,则可以:

root.xpath('.//table:table', namespaces=root.nsmap)

findall(path)接受{namespace}name语法,而不是namespace:name。因此,在将path传递给findall()之前,应该使用命名空间字典将其预处理为{namespace}name形式。

下面是一种获取XML文档中所有名称空间的方法(假设没有前缀冲突)。

我在解析XML文档时使用这个,因为我事先知道名称空间url是什么,而且只知道前缀。

        doc = etree.XML(XML_string)

        # Getting all the name spaces.
        nsmap = {}
        for ns in doc.xpath('//namespace::*'):
            if ns[0]: # Removes the None namespace, neither needed nor supported.
                nsmap[ns[0]] = ns[1]
        doc.xpath('//prefix:element', namespaces=nsmap)

也许首先要注意的是名称空间 定义在元素级,而不是文档级。

但是,大多数情况下,所有名称空间都在文档的 根元素(office:document-content在这里),这节省了我们对其进行分析以收集内部的xmlns作用域。

然后,元素nsmap包括:

  • 默认命名空间,前缀为None(并非总是如此)
  • 所有祖先名称空间,除非重写。

如果如ChrisR所述,不支持默认名称空间, 你可以用dict comprehension过滤掉它 用更简洁的表达。

xpath和 ElementPath


所以这里的代码可以用来获取第一个表的所有行 (用lxml=3.4.2测试):

import zipfile
from lxml import etree

# Open and parse the document
zf = zipfile.ZipFile('spreadsheet.ods')
tree = etree.parse(zf.open('content.xml'))

# Get the root element
root = tree.getroot()

# get its namespace map, excluding default namespace
nsmap = {k:v for k,v in root.nsmap.iteritems() if k}

# use defined prefixes to access elements
table = tree.find('.//table:table', nsmap)
rows = table.findall('table:table-row', nsmap)

# or, if xpath is needed:
table = tree.xpath('//table:table', namespaces=nsmap)[0]
rows = table.xpath('table:table-row', namespaces=nsmap)

相关问题 更多 >

    热门问题