2010-11-18 97 views
19

我正试图解析OpenOffice ODS电子表格中的内容。 ods格式基本上只是一个包含大量文档的zip文件。电子表格的内容存储在'content.xml'中。如何在lxml中使用find/findall来使用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: ':' 
+0

您是否尝试过使用Python API OpenOffice的处理电子表格? – jfs 2010-11-18 01:41:45

+0

您好我正在使用etree.QName来访问名称空间的元素和属性。它在命名空间字典的帮助下是一个整洁的方式,它也适用于find和findall方法。有关更多信息,请参阅:http://lxml.de/tutorial.html#namespaces – 2015-12-19 10:51:40

回答

16

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

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

findall(path)接受{namespace}name语法而不是namespace:name。因此path应该使用名称空间字典预处理到{namespace}name表格,然后将其传递到findall()

+0

有趣,但似乎存在较低级别的问题:table.xpath('.// table:table-row',nsmap = table.nsmap) *** XPathResultError:未知返回类型:字典 – saffsd 2010-11-19 04:19:41

+0

@saffsd:注意:* namespaces = * not * nsmap = *。尝试:'root.xpath('.// table:table-row',namespaces = {'table':'urn:oasis:names:tc:opendocument:xmlns:table:1.0'})' – jfs 2010-11-19 14:11:41

6

以下是获取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) 
5

也许注意到的第一件事情是,命名空间 在元水平,不文档级别定义。

大多数情况下,虽然,所有的命名空间在文档的 根元素(office:document-content这里),它让我们可以分析它全部收集内xmlns范围声明。

然后,一个元件nsmap包括:

  • 默认名称空间,与None前缀(不总是)
  • 所有祖先的命名空间,除非被覆盖。

如果像ChrisR mentionned,默认的命名空间不支持, 可以使用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) 
+0

如果你需要一个nsmap,包括默认的命名空间,使用(Python 3):'nsmap = {k如果k不是None其他'默认':v for k,v in root.nsmap.items()} – skelliam 2018-03-01 20:39:04

+0

对于Python 3重命名iteritems )以上只是项目()。 – skelliam 2018-03-01 20:40:01