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

あなたはスプレッドシートを処理するためにOpenOfficeのためのPython APIを使用しようとしたことがありますか? – jfs

+0

こんにちは私はetree.QNameを使用して、名前空間で要素と属性にアクセスしています。ネームスペースの辞書の助けを借りてきちんとした方法で、findとfindallメソッドでも動作します。詳細については、http://lxml.de/tutorial.html#namespaces –

答えて

16

root.nsmaptable名前空間接頭辞が含まれているなら、あなたはできます

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

findall(path)ではなくnamespace:name{namespace}name構文を受け付けます。したがって、pathは、に渡す前に、{namespace}name形式の名前空間辞書を使用して前処理する必要があります。

+0

を参照してください。面白いですが、下位レベルの問題があるようです:table.xpath( '.// table:table-row'、nsmap = table.nsmap) *** XPathResultError:不明な戻り値の型:dict – saffsd

+0

@saffsd:注:* namespaces = * not * nsmap = *。 'root.xpath(' .// table:table-row '、namespaces = {'テーブル ':' urn:オアシス:名前:tc:opendocument:xmlns:table:1.0 '})' – jfs

6

XML文書内のすべての名前空間を取得する方法です(プレフィックスの競合がないと仮定します)。

私は、名前空間のURLとプレフィックスだけを事前に知っているXML文書を解析するときにこれを使用します。

 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

たぶん、注意すべき最初の事は名前空間 が要素レベル、ないドキュメントレベルで定義されているということです。ほとんどの場合

しかし、すべての名前空間は、私たちは、内側xmlnsスコープを収集するためにそれをすべて解析し保存した文書の ルート要素(ここではoffice:document-content)、で宣言されています。

その後要素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: 'nsmap = {kがk以外であればkはなし} else 'default':vの場合はv、root.nsmap.items()の場合は' – skelliam

+0

Python 3の場合はiteritems )を項目()に置き換えます。 – skelliam

関連する問題