2010-11-18 32 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