Seite 1 von 1

Xml: zu bestimten Knoten hüpfen

Verfasst: Sonntag 18. Oktober 2009, 15:03
von kimx
Hallo erstmal. Habe folgendes Problem: ich muss in einer XML-Datei zu einem Knoten springen dessen Argument einen bestimmten Wert enthält. Hier ein Beispiel:

Code: Alles auswählen

<categories>
  <category>
    <a id=1 />
    <a id=2 />
  </category>
</categories>
Ich müsste zum Knoten mit der id=2. Habe mit lxml probiert. Auf dem Tutoriel auf der lxml Homepage ist ein solches Fallbeispiel angeführt:

Code: Alles auswählen

print(root.findall(".//a[@x]")[0].tag)
Aber in diesem Beispiel kann man den Wert des Arguments nicht mitgeben.
Gibt es da eine Methode?

Danke im voraus
kim

Verfasst: Sonntag 18. Oktober 2009, 15:08
von Hyperion
Dazu solltest Du Dir mal etwas zu XPath durchlesen:
http://de.wikipedia.org/wiki/XPath

Dort findet man schnell, dass man bei Prädikaten durchaus auf einen Wert vergleichen kann:

Code: Alles auswählen

print(root.findall(".//a[@x=3]")[0].tag)

Verfasst: Sonntag 18. Oktober 2009, 17:00
von BlackJack
Und in der Dokumentation zu `lxml` findet man dann auch wie man die "3" variabel gestalten kann, ohne da mit Zeichenkettenformatierung hantieren zu müssen.

Verfasst: Sonntag 18. Oktober 2009, 18:21
von kimx
habe es wie folgt schon probiert:

Code: Alles auswählen

print (root.findall('.//category[@id="altri_video"]')[0].tag)
aber bekomme folgende Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "test.py", line 31, in <module>
    print root.findall('.//category[@id="altri_video"]')
  File "etree.pyx", line 1085, in etree._Element.findall
  File "/usr/lib/python2.5/site-packages/lxml/_elementpath.py", line 193, in fin                                                                             dall
    return _compile(path).findall(element)
  File "/usr/lib/python2.5/site-packages/lxml/_elementpath.py", line 171, in _co                                                                             mpile
    p = Path(path)
  File "/usr/lib/python2.5/site-packages/lxml/_elementpath.py", line 88, in __in                                                                             it__
    "expected path separator (%s)" % (op or tag)
SyntaxError: expected path separator ([)

Verfasst: Sonntag 18. Oktober 2009, 19:05
von problembär
Hallo,

Dein xml ist nicht wohlgeformt.

Hier mal auf die altmodische Art (die die einzige ist, die bei mir klappt (da alte Installation)):

Code: Alles auswählen

import xml.dom.minidom

stuff = """<categories>
  <category>
    <a id="1" />
    <a id="2" />
    <a id="3">Hallo</a>
  </category>
</categories>"""

dommod = xml.dom.minidom.parseString(stuff)

nodelist = dommod.getElementsByTagName("a")

for node in nodelist:

    if node.attributes["id"].nodeValue == "3":
        print
        print "Node found: " + node.localName
        for c in node.childNodes:
            if c.nodeType == node.TEXT_NODE:
                print "Text-value: " + c.nodeValue
        at = []
        for i in node.attributes.keys():
            at.append(i)
        print "Attributes: " + ",".join(at)
        print
Gruß

Verfasst: Sonntag 18. Oktober 2009, 19:07
von Hyperion
Also bei mir klappt's eben so auch mit lxml:

Code: Alles auswählen

In [42]: data = u'<categories><category><a id="1"/><a id="2"/></category></categories>'
In [43]: root = etree.fromstring(data)

In [46]: root.findall('.//category/a[@id="2"]')
Out[46]: [<Element a at 1b036f0>]

In [47]: root.findall('.//category/a[@id="1"]')
Out[47]: [<Element a at 1b030c0>]

Verfasst: Sonntag 18. Oktober 2009, 19:13
von kimx
Das XML ist nur ein Beispiel. Habe es dort gar nicht getestet. Mein Fehler. Dies wäre das richtige:
http://pastebin.com/m563f23bc

Ein Beispiel könnte sein id="altri_video"

Verfasst: Sonntag 18. Oktober 2009, 19:26
von Hyperion
Imho ist das Dokument kaputt:

Code: Alles auswählen

In [55]: root = etree.parse(f)
ERROR: An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line statement', (16963, 0))

ERROR: An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line statement', (18148, 0))

ERROR: An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line statement', (18118, 0))

ERROR: An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line statement', (18235, 0))

ERROR: An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line statement', (19036, 0))

---------------------------------------------------------------------------
XMLSyntaxError                            Traceback (most recent call last)

C:\Dokumente und Einstellungen\nelson\Eigene Dateien\Downloads\<ipython console>
 in <module>()

C:\Programme\Python26\lib\site-packages\lxml-2.2.2-py2.6-win32.egg\lxml\etree.py
d in lxml.etree.parse (src/lxml/lxml.etree.c:49590)()

C:\Programme\Python26\lib\site-packages\lxml-2.2.2-py2.6-win32.egg\lxml\etree.py
d in lxml.etree._parseDocument (src/lxml/lxml.etree.c:71423)()

C:\Programme\Python26\lib\site-packages\lxml-2.2.2-py2.6-win32.egg\lxml\etree.py
d in lxml.etree._parseFilelikeDocument (src/lxml/lxml.etree.c:71733)()

C:\Programme\Python26\lib\site-packages\lxml-2.2.2-py2.6-win32.egg\lxml\etree.py
d in lxml.etree._parseDocFromFilelike (src/lxml/lxml.etree.c:70648)()

C:\Programme\Python26\lib\site-packages\lxml-2.2.2-py2.6-win32.egg\lxml\etree.py
d in lxml.etree._BaseParser._parseDocFromFilelike (src/lxml/lxml.etree.c:67944)(
)

C:\Programme\Python26\lib\site-packages\lxml-2.2.2-py2.6-win32.egg\lxml\etree.py
d in lxml.etree._ParserContext._handleParseResultDoc (src/lxml/lxml.etree.c:6382
0)()

C:\Programme\Python26\lib\site-packages\lxml-2.2.2-py2.6-win32.egg\lxml\etree.py
d in lxml.etree._handleParseResult (src/lxml/lxml.etree.c:64741)()

C:\Programme\Python26\lib\site-packages\lxml-2.2.2-py2.6-win32.egg\lxml\etree.py
d in lxml.etree._raiseParseError (src/lxml/lxml.etree.c:64084)()

XMLSyntaxError: Document is empty, line 1, column 1
Hast Du das laden und parsen können?

Verfasst: Sonntag 18. Oktober 2009, 19:29
von kimx
Ich habs von dieser Seite:
http://www.video.mediaset.it/category/categories.sxml

Bei mir gings.

Verfasst: Sonntag 18. Oktober 2009, 19:34
von Hyperion
Also damit klappt's bei mir:

Code: Alles auswählen

In [65]: f = open("categories.sxml.xml", "r")

In [66]: root = etree.parse(f)

In [67]: root.findall('.//category[@id="altri_video"]')
Out[67]: [<Element category at 19b92d0>]
Wo liegt jetzt das Problem?

Verfasst: Sonntag 18. Oktober 2009, 19:39
von BlackJack
@kimx: Die Methode für XPath heisst ja auch `xpath()` und nicht `findall()`. Hier auch gleichmal mit einem Beispiel, wie man das parametrisieren kann:

Code: Alles auswählen

In [29]: doc.xpath('.//category[@id=$id]', id='altri_video')
Out[29]: [<Element category at 848adec>]

Verfasst: Sonntag 18. Oktober 2009, 19:43
von kimx
Bei mir gehts leider nicht. Habe Python 2.5.2. Wieder folgender Fehler:

Code: Alles auswählen

Traceback (most recent call last):
  File "tt.py", line 7, in <module>
    root.findall('.//category[@id="altri_video"]')
  File "etree.pyx", line 1378, in etree._ElementTree.findall
  File "etree.pyx", line 1085, in etree._Element.findall
  File "/usr/lib/python2.5/site-packages/lxml/_elementpath.py", line 193, in findall
    return _compile(path).findall(element)
  File "/usr/lib/python2.5/site-packages/lxml/_elementpath.py", line 171, in _compile
    p = Path(path)
  File "/usr/lib/python2.5/site-packages/lxml/_elementpath.py", line 88, in __init__
    "expected path separator (%s)" % (op or tag)
SyntaxError: expected path separator ([)
Mit folgendem Code:

Code: Alles auswählen

from lxml import etree

f = open("categories.sxml", "r")

root = etree.parse(f)

root.findall('.//category[@id="altri_video"]')

Verfasst: Sonntag 18. Oktober 2009, 19:55
von kimx
Jetzt gehts. Habe leider den Post mit der korrekten Methode nicht gesehen.
Sehr gutes Forum, danke nochmal

kimx

Verfasst: Sonntag 18. Oktober 2009, 23:46
von problembär
Nur der Vollständigkeit halber:

Code: Alles auswählen

import xml.dom.minidom

dommod = xml.dom.minidom.parse("m563f23bc.txt")

def getAbsPath(node):
    a = [node.nodeName]
    p = node.parentNode
    while p.nodeName != "#document":
        a.append(p.nodeName)
        p = p.parentNode
    a.reverse()
    return "/".join(a)

def dokument(parent):

    for node in parent.childNodes:
        if node.attributes and node.attributes.has_key("id") and node.attributes["id"].nodeValue == "altri_video":
            print getAbsPath(node)
        dokument(node)
 
dokument(dommod)
Schon etwas umständlich :roll:.

Gruß