Frage

Ich versuche zu Parse Inhalt in einer Tabelle Openoffice ODS. Das ods Format ist im Wesentlichen nur eine Zip-Datei mit einer Reihe von Dokumenten. Der Inhalt der Tabelle wird in ‚content.xml‘ gespeichert.

import zipfile
from lxml import etree

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

Der Inhalt der Tabelle ist in einer Zelle:

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

Wir können auch direkt für die Zeilen gehen:

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

Die einzelnen Elemente kennen die Namensräume:

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

Wie verwende ich die Namensräume direkt in find / findall?

Die offensichtliche Lösung funktioniert nicht.

Der Versuch, die Zeilen aus der Tabelle zu erhalten:

>>> 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: ':'
War es hilfreich?

Lösung

Wenn root.nsmap enthält das table Namespacepräfix dann können Sie:

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

findall(path) akzeptiert {namespace}name Syntax statt namespace:name. Daher path vorverarbeitet werden sollte Namespace-Wörterbuch zum {namespace}name Formular, bevor es zu findall() vorbei.

Andere Tipps

Hier ist ein Weg, um alle Namespaces in XML-Dokument zu erhalten (und unter der Annahme gibt es keinen Präfix-Konflikt).

Ich benutze dies, wenn XML-Dokumente parsen, wo ich im Voraus tun, was die Namespace-URLs sind, und nur das Präfix.

        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)

Vielleicht ist das erste, was zu beachten ist, dass die Namespaces definiert ist, in Element-Ebene , nicht Dokumentebene.

Am häufigsten jedoch alle Namensräumen sind in dem Dokument deklariert Wurzelelement (office:document-content hier), die uns es Parsen speichert alle inneren xmlns Bereiche zu sammeln.

Dann ein Element nsmap enthält:

  • ein Standard-Namespace, mit None Präfix (nicht immer)
  • alle Vorfahren Namensraum, es sei denn, außer Kraft gesetzt.

Wenn, wie ChrisR erwähnt, wird der Standard-Namespace nicht unterstützt, Sie können ein dict Verständnis filtern verwenden out in einem kompakteren Ausdruck.

Sie haben eine etwas andere Syntax für XPath und ElementPath .


Also hier ist der Code, den Sie könnten alle Ihre ersten Tabellenzeilen benutzen Sie (Getestet mit: 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)

etree nicht Namespace-Elemente finden, wenn es keine xmlns Definitionen in der XML-Datei sind. Zum Beispiel:

import lxml.etree as etree

xml_doc = '<ns:root><ns:child></ns:child></ns:root>'

tree = etree.fromstring(xml_doc)

# finds nothing:
tree.find('.//ns:root', {'ns': 'foo'})
tree.find('.//{foo}root', {'ns': 'foo'})
tree.find('.//ns:root')
tree.find('.//ns:root')

Manchmal ist, dass die Daten, die Sie gegeben sind. Also, was können Sie tun, wenn es kein Namensraum ist?

Meine Lösung:. Ein Add

import lxml.etree as etree

xml_doc = '<ns:root><ns:child></ns:child></ns:root>'
xml_doc_with_ns = '<ROOT xmlns:ns="foo">%s</ROOT>' % xml_doc

tree = etree.fromstring(xml_doc_with_ns)

# finds what you're looking for:
tree.find('.//{foo}root')
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top