ElementTree und XML Fragen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
MoR4euZ
User
Beiträge: 34
Registriert: Mittwoch 18. Oktober 2006, 21:21
Wohnort: Essen
Kontaktdaten:

Guten Abend

Ich habe mich heute mal mit ElementTree beschäftigt.
Nach Stunden des ausprobierens und Tutorials lesen sind noch etliche Fragen in Bezug auf ElementTree und XML geblieben. :(

Ich habe folgende XML Datei:

Code: Alles auswählen

<?xml version="1.0" encoding="utf-8" ?>
<Processes>
    <Process id="1">
        <LongName>Calculator</LongName>
        <ShortName>calc</ShortName>
        <Launch>C:\WINDOWS\system32\calc.exe</Launch>
        <RunTime></RunTime>
    </Process>
    <Process id="2">
        <LongName>WordPad</LongName>
        <ShortName>rtf</ShortName>
        <Launch>C:\WINDOWS\system32\write.exe</Launch>
        <RunTime></RunTime>
    </Process>
    <Process id="3">
        <LongName>Solitaer</LongName>
        <ShortName>sol</ShortName>
        <Launch>C:\WINDOWS\system32\sol.exe</Launch>
        <RunTime></RunTime>
    </Process>
</Processes>
Daraus möchte ich alle LongName-Tags
und jedes Launch-Tag einzeln heraus ziehen.

Alle zusammen klapt schonmal:

Code: Alles auswählen

from xml.etree import ElementTree as etree

root = etree.parse('process.xml').getroot()
    
for element in root.findall('.///LongName'):
    print element.text
for element in root.getiterator('Launch'):
    print element.text

Code: Alles auswählen

Calculator
WordPad
Solitaer
C:\WINDOWS\system32\calc.exe
C:\WINDOWS\system32\write.exe
C:\WINDOWS\system32\sol.exe
Zu den Fragen:

Wie bekomme ich die Launch-Tags separat aus der XML Datei bei gleichem Tagnamen?

Wie kann ich die RunTime-Tags seperat schreiben?

Kann ich die id's in der XML Datei ansprechen?

Wie sieht es mit Umlauten in der XML Datei aus (bei Solitär)?

Muss man in der XML Datei mit TABs einrücken oder reicht da auch ein oder zwei SPACEs?

Wie bekomme ich mit python-Bordmitteln aus der XML Datei eine HTML Datei?


Ich hoffe die Fragen sind alle verständlich
Bin leicht verwirrt ;)
BlackJack

MoR4euZ hat geschrieben:Daraus möchte ich alle LongName-Tags
und jedes Launch-Tag einzeln heraus ziehen.

Alle zusammen klapt schonmal:

Code: Alles auswählen

from xml.etree import ElementTree as etree

root = etree.parse('process.xml').getroot()
    
for element in root.findall('.///LongName'):
    print element.text
for element in root.getiterator('Launch'):
    print element.text
Hier ziehst Du doch die einzelnen Elemente einzeln heraus!? Ich versteh die Frage nicht so ganz.
Kann ich die id's in der XML Datei ansprechen?
Am einfachsten wahrscheinlich indem Du den Baum mit `XMLID()` erzeugst. Die Funktion gibt neben dem Baum auch gleich ein Dictionary mit IDs zurück:

Code: Alles auswählen

In [63]: print a
<?xml version="1.0" encoding="utf-8" ?>
<Processes>
   <Process id="1">
       <LongName>Calculator</LongName>
       <ShortName>calc</ShortName>
       <Launch>C:\WINDOWS\system32\calc.exe</Launch>
       <RunTime></RunTime>
   </Process>
   <Process id="2">
       <LongName>WordPad</LongName>
       <ShortName>rtf</ShortName>
       <Launch>C:\WINDOWS\system32\write.exe</Launch>
       <RunTime></RunTime>
   </Process>
   <Process id="3">
       <LongName>Solitär</LongName>
       <ShortName>sol</ShortName>
       <Launch>C:\WINDOWS\system32\sol.exe</Launch>
       <RunTime></RunTime>
   </Process>
</Processes>


In [64]: doc, ids = etree.XMLID(a)

In [65]: ids
Out[65]:
{'1': <Element Process at -486fdc74>,
 '2': <Element Process at -486fd9d4>,
 '3': <Element Process at -486fd814>}
Wie sieht es mit Umlauten in der XML Datei aus (bei Solitär)?
Das Textattribut enthält ein Unicode-Objekt falls der Textknoten etwas ausserhalb von ASCII enthält:

Code: Alles auswählen

In [76]: ids['3'].getchildren()[0].text
Out[76]: u'Solit\xe4r'
Muss man in der XML Datei mit TABs einrücken oder reicht da auch ein oder zwei SPACEs?
Das kannst Du machen wie Du willst. Überall wo kein Text erwartet wird, kannst Du so viel oder wenig "whitespace" einfügen wie du magst.
Wie bekomme ich mit python-Bordmitteln aus der XML Datei eine HTML Datei?
Das ist ziemlich vage. Wie soll das denn in HTML aussehen?
Benutzeravatar
MoR4euZ
User
Beiträge: 34
Registriert: Mittwoch 18. Oktober 2006, 21:21
Wohnort: Essen
Kontaktdaten:

danke für deine antwort blackjack
es war sehr hilfreich
danach habe ich gesucht :)

Code: Alles auswählen

from xml.etree import ElementTree as etree

source = """\
<?xml version="1.0" encoding="utf-8" ?>
<Processes>
    <Process id="1">
        <LongName>Calculator</LongName>
        <ShortName>calc</ShortName>
        <Launch>C:\WINDOWS\system32\calc.exe</Launch>
        <RunTime></RunTime>
    </Process>
    <Process id="2">
        <LongName>WordPad</LongName>
        <ShortName>rtf</ShortName>
        <Launch>C:\WINDOWS\system32\write.exe</Launch>
        <RunTime></RunTime>
    </Process>
    <Process id="3">
        <LongName>Solitaer</LongName>
        <ShortName>sol</ShortName>
        <Launch>C:\WINDOWS\system32\sol.exe</Launch>
        <RunTime></RunTime>
    </Process>
</Processes>"""

doc, ids = etree.XMLID(source)

path = ids['1'].getchildren()[2].text

print path
wie kann ich nun meine externe xml datei wieder einbinden?

bei "source = etree.parse('process.xml','r')" bekomme ich folgenden traceback

Code: Alles auswählen

Traceback (most recent call last):
  File "pyfire6.py", line 7, in <module>
    source = etree.parse('process.xml','r')
  File "C:\Python25\lib\xml\etree\ElementTree.py", line 862, in parse
    tree.parse(source, parser)
  File "C:\Python25\lib\xml\etree\ElementTree.py", line 586, in parse
    parser.feed(data)
AttributeError: 'str' object has no attribute 'feed'
wie muss ich die xml datei nun einbinden?
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

parse(source, parser=None) [#]

Parses an XML document into an element tree.

source
A filename or file object containing XML data.
parser
An optional parser instance. If not given, the standard XMLTreeBuilder parser is used.
Returns:
An ElementTree instance
Du willst etree.parse('dateiname.xml') ohne zweiten Parameter. Oder etree.ElementTree(file='dateiname.xml').
Benutzeravatar
MoR4euZ
User
Beiträge: 34
Registriert: Mittwoch 18. Oktober 2006, 21:21
Wohnort: Essen
Kontaktdaten:

hmm funktioniert alles nicht :(
liegt sicher an der xmlid fuktion die ich nicht so ganz verstehe ;)
BlackJack

Die Funktion möchte eine Zeichenkette haben. Du musst einfach die XML Datei komplett lesen und das Ergebnis an die Funktion übergeben.
Benutzeravatar
MoR4euZ
User
Beiträge: 34
Registriert: Mittwoch 18. Oktober 2006, 21:21
Wohnort: Essen
Kontaktdaten:

ok danke
das mit dem string habe ich hinbekommen :)

aber wie schreibe ich mit XMLID daten in die xmldatei?

http://codespeak.net/pipermail/lxml-che ... 00700.html

mehr brauchbares habe ich leider nicht gefunden
habe schon probiert es bei mir anzuwenden aber ohne erfolg bis jetzt

oder sollte ich mal lxml einrichten?
XMLID stammt ja woll von dort ab oder?
BlackJack

MoR4euZ hat geschrieben:aber wie schreibe ich mit XMLID daten in die xmldatei?
Du bekommst ein ganz normales `Element`-Objekt und ein Dictionary zurück. Das `Element` kannst Du in einen `ElementTree` stecken und dann hast Du eine `write()`-Methode.

Code: Alles auswählen

In [15]: a = etree.XMLID('<test />')[0]

In [16]: b = etree.ElementTree(a)

In [17]: b.write(sys.stdout)
Benutzeravatar
MoR4euZ
User
Beiträge: 34
Registriert: Mittwoch 18. Oktober 2006, 21:21
Wohnort: Essen
Kontaktdaten:

hmm sorry ich verstehe dein beispiel leider nicht irgentwie fehlen mir noch etliche basics dafür :/

hier mal ein komplettes beispiel (ohne eigene funktionen)

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import time
from decimal import Decimal as deci
from xml.etree import ElementTree as etree

#read path and old_runtime
f = file('process.xml', 'r')
s = f.read()
f.close()
doc, ids = etree.XMLID(s)
path = ids['1'].getchildren()[2].text
old_runtime = ids['1'].getchildren()[3].text 
print 'alte RunTime: ', old_runtime 

#runtime detectin
start_time = time.time()
os.system(path)
runtime = time.time() - start_time
print 'ermittelte RunTime: ', runtime

#runtime addition
new_runtime = deci(str(runtime)) + deci(old_runtime)
print 'neue RunTime: ', new_runtime

#write new_runtime
new_runtime = etree.XMLID('<RunTime />')[0]
b = etree.ElementTree(new_runtime)
b.write(sys.stdout)

Code: Alles auswählen

<?xml version="1.0" encoding="utf-8" ?>
<?xml-stylesheet type="text/xsl" href="Process.xsl"?>
<Process_db>
    <Process id="1">
        <LongName>Calculator</LongName>
        <ShortName>calc</ShortName>
        <Path>C:\WINDOWS\system32\calc.exe</Path>
        <RunTime>19.999999999</RunTime>
    </Process>
    <Process id="2">
        <LongName>WordPad</LongName>
        <ShortName>rtf</ShortName>
        <Path>C:\WINDOWS\system32\write.exe</Path>
        <RunTime></RunTime>
    </Process>
    <Process id="3">
        <LongName>Solitaer</LongName>
        <ShortName>sol</ShortName>
        <Path>C:\WINDOWS\system32\sol.exe</Path>
        <RunTime></RunTime>
    </Process>
</Process_db>
was fehlt oder passt nicht bei der write-methode?
habe etliche andere sachen probiert leider ohne erfolg
bei mir bleibt die xml-datei unverändert oder der inhalt wird ganz gelöscht/überschrieben
BlackJack

MoR4euZ hat geschrieben:was fehlt oder passt nicht bei der write-methode?
Du erzeugst ein leeres RunTime-Element und speicherst das in der Datei `sys.stdout`, also die Ausgabe des Programms. Was erwartest Du denn?

Wenn Du das eingelesene XML-Dokument verändern willst, dann musst Du den Inhalt des eingelesenen RunTime-Elements verändern und den eingelesenen und nun veränderten Baum in eine Datei speichern.
Benutzeravatar
MoR4euZ
User
Beiträge: 34
Registriert: Mittwoch 18. Oktober 2006, 21:21
Wohnort: Essen
Kontaktdaten:

BlackJack hat geschrieben:Wenn Du das eingelesene XML-Dokument verändern willst, dann musst Du den Inhalt des eingelesenen RunTime-Elements verändern und den eingelesenen und nun veränderten Baum in eine Datei speichern.
ok den satz verstehe ich :)
nur bekomme ich es nicht umgesetzt :roll:

die lösung bleibt ein dreizeiler ja?
aus welcher shell kommen überhaupt deine beispiele?

ich bin sowas von verwirrt und frustriert
für heute bin ich fertig :!:
egal ich schnappe mir morgen erstmal mein python buch sonst wird das nie was :?

danke und gute nacht
BlackJack

MoR4euZ hat geschrieben:
BlackJack hat geschrieben:Wenn Du das eingelesene XML-Dokument verändern willst, dann musst Du den Inhalt des eingelesenen RunTime-Elements verändern und den eingelesenen und nun veränderten Baum in eine Datei speichern.
ok den satz verstehe ich :)
nur bekomme ich es nicht umgesetzt :roll:

die lösung bleibt ein dreizeiler ja?
Du hast jetzt schon deutlich mehr als 3 Zeilen da stehen. ;-)

Ich habe mit den drei Zeilen nur demonstrieren wollen wie man eine XML Datei schreibt, wenn man ein `Element`-Objekt hat. In der ersten Zeile habe ich ein beliebiges `Element`-Objekt erzeugt, das hast Du ja schon. In der zweiten Zeile habe ich es in einen `ElementTree` "verpackt", weil der eine Methode hat um den Baum in eine offene Datei zu schreiben. Was dann in der letzten Zeile mit `sys.stdout` demonstriert wurde. Du müsstest da natürlich eine Datei öffnen und übergeben und hinterher wieder schliessen.
aus welcher shell kommen überhaupt deine beispiele?
IPython.
Benutzeravatar
MoR4euZ
User
Beiträge: 34
Registriert: Mittwoch 18. Oktober 2006, 21:21
Wohnort: Essen
Kontaktdaten:

ich habe mal wieder die halbe nacht mit python verbracht :)
auch mit elementtree habe ich weiter gekämpft ;)

klapt leider immer noch nicht :(
ich bekomme immer attribute errors vom elementinterface
aber wieso?

Code: Alles auswählen

import sys
from xml.etree import ElementTree as etree

new_runtime = 11.111111111 

f = file('process.xml', 'r')
s = f.read()
root, ids = etree.XMLID(s)

doc = etree.ElementTree(file='process.xml').getroot()
rtime = root.ids['1'].getchildren()[3]
rtime.clear()
rtime.text = new_runtime

doc.write(sys.stdout)
f.close()

Code: Alles auswählen

doc.write(sys.stdout)
AttributeError: _Elementinterface instance has no attribute to write

rtime = root.ids['1'].getchildren()[3]
AttributeError: _Elementinterface instance has no attribute 'ids'
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

`ids` hast du als eigene Variable entgegengenommen, rufst aber stattdessen mit `root.ids`ein Objektattribut auf. Mit genauem Hinsehen kann man da auch selbst drauf kommen.
Benutzeravatar
MoR4euZ
User
Beiträge: 34
Registriert: Mittwoch 18. Oktober 2006, 21:21
Wohnort: Essen
Kontaktdaten:

ok hast recht root.ids war nur ein verzweifelter versuch von mir ;)
der attributeerror ist nun weg
mit doc.getroot() kommt kein attributeerror mehr

ABER :D

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from xml.etree import ElementTree as etree

xmldoc = 'process.xml'
new_runtime = 11.111111111 

f = file(xmldoc, 'r')
s = f.read()
root, id = etree.XMLID(s)

doc = etree.ElementTree(file=xmldoc)
doc.getroot()
rtime = id['1'].getchildren()[3]
rtime.clear()
rtime.text = new_runtime

doc.write(file=xmldoc)
f.close()

mein xmlfile wird leider nicht richtig geschrieben

es stimmt sicher was in zeile 15 nicht
nur keine ahnung was :(

müsste es nicht
rtime = doc.getroot().id['1'].getchildren()[3]
lauten?

Code: Alles auswählen

<Process_db>
    <Process id="1">
        <LongName>Calculator</LongName>
        <ShortName>calc</ShortName>
        <Path>C:\WINDOWS\system32\calc.exe</Path>
        <RunTime>19.999999999</RunTime>
    </Process>
    <Process id="2">
        <LongName>WordPad</LongName>
        <ShortName>rtf</ShortName>
        <Path>C:\WINDOWS\system32\write.exe</Path>
        <RunTime />
    </Process>
    <Process id="3">
        <LongName>Solitaer</LongName>
        <ShortName>sol</ShortName>
        <Path>C:\WINDOWS\system32\sol.exe</Path>
        <RunTime />
    </Process>
</Process_db>
wieso wird das roottag (</Process_db>) mit doc.getroot() nicht erkannt?
die ersten beiden zeilen sind weg :)
und ich will ja nur den ersten wert im RunTime tag ändern nicht alle drei :(

das beispiel listing5.py (ohne XMLID) von xml.com klappt wunderbar
http://www.xml.com/pub/a/2003/02/12/py-xml.html?page=2
BlackJack

MoR4euZ hat geschrieben:ok hast recht root.ids war nur ein verzweifelter versuch von mir ;)
Du solltest nicht verzweifelt etwas versuchen, das nennt man "programming by accident", sondern verstehen was Du da tust.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from xml.etree import ElementTree as etree

xmldoc = 'process.xml'
new_runtime = 11.111111111 

f = file(xmldoc, 'r')
s = f.read()
root, id = etree.XMLID(s)

doc = etree.ElementTree(file=xmldoc)
doc.getroot()
rtime = id['1'].getchildren()[3]
rtime.clear()
rtime.text = new_runtime

doc.write(file=xmldoc)
f.close()
Du bindest den Namen `xmldoc` an den Dateinamen. Dann öffnest Du die Datei zum lesen und liest sie komplett ein. Den Dateiinhalt parst Du mit `XMLID()` und erhältst das Wurzelelement, mit allem was da dran hängt, und ein Dictionary das ID-Namen direkt auf die entsprechenden `Element`-Objekte abbildet.

Dann erzeugst Du mit `ElementTree()` *noch* einmal eine Objekthierarchie von `Element`-Objekten und bindest diese an den Namen `doc`. Davon ermittelst Du das Wurzelelement und machst nichts damit. Aber auch so wirklich gar nichts, nicht mal an einen Namen binden.

Dann holst Du Dir via ID ein `Element`-Objekt aus dem *ersten* Baum den Du eingelesen hast, löscht dessen Inhalt und setzt den Text auf die neue Laufzeit.

Und zum Schluss schreibst Du den Baum, den Du *nicht* verändert hast, wieder in eine Datei zurück und schliesst die Datei die die ganze Zeit über noch zum Lesen geöffnet war. Dabei kann nichts passieren wenn Du danach nicht wieder versuchst was zu lesen, aber es ist mindestens unsauber die so lange ohne Grund offen zu halten.
Benutzeravatar
MoR4euZ
User
Beiträge: 34
Registriert: Mittwoch 18. Oktober 2006, 21:21
Wohnort: Essen
Kontaktdaten:

BlackJack hat geschrieben:
MoR4euZ hat geschrieben:ok hast recht root.ids war nur ein verzweifelter versuch von mir ;)
Du solltest nicht verzweifelt etwas versuchen, das nennt man "programming by accident", sondern verstehen was Du da tust.
nett gesagt :)
so ist es :(
oder auch "try and error" genannt

ich will das entlich gelöst bekommen koste es was es wolle :evil:
danach lese ich auch braff mein python buch zuende ;)
noch habe ich genug motivation für das try and error verfahren
aber sicher nicht mehr lange hrhr

ich habe alles nochmal durchgedacht

Code: Alles auswählen

from xml.etree import ElementTree as etree

xmldoc = 'process.xml'
new_runtime = 99.111111111 

# Lesen
f = file(xmldoc, 'r')
s = f.read()
doc, id = etree.XMLID(s)
f.close()

# Voher
print 'RunTime vorher: ', id['1'].getchildren()[3].text #11.999999999
print 'RunTime Baum vorher:', s #11.999999999

# Veraendern
## Element verändern
id['1'].getchildren()[3].text = new_runtime
## Verändertes Element in Baum schreiben???
## mit etree.ElementTree(?) ???

# Nachher
print 'RunTime nachher: ', id['1'].getchildren()[3].text #99.111111111
print 'RunTime Baum nachher:', s #99.111111111 ist leider immernoch 11.999999999

# Schreiben
f = file(xmldoc, 'w')
f.write(s)
f.close()
das runtime element bekomme ich verändert aber ich bekomme das veränderte runtime element nicht in den baum (s) zurück geschrieben

das müstte doch mit etree.ElementTree() gehen oder nicht?
ich komme leider immer noch nicht drauf wie :(

stimmt sonst alles andere?

gute nacht
BlackJack

Stimmt ja schon fast. `s` ist an die Zeichenkette gebunden, die Du am Anfang eingelesen hast. Die veränderst Du nicht, Du veränderst ein `Element`-Objekt in der Objekthierarchie die an den Namen `doc` gebunden ist. Und genau diese musst Du in eine Datei schreiben, nachdem Du sie verändert hast.

Da `doc` an ein `Element`-Objekt gebunden ist, welches keine `write()`-Methode hat, musst Du dieses Element in einen `ElementTree` stecken und auf dem dann die `write()`-Methode aufrufen. Das war's dann auch schon.
Benutzeravatar
MoR4euZ
User
Beiträge: 34
Registriert: Mittwoch 18. Oktober 2006, 21:21
Wohnort: Essen
Kontaktdaten:

ja habs
DANKE! :)

ich hoffe es ist alles richtig

ein manko hat die sache allerdings noch :?

die ersten beiden zeilen in der xmldatei (über dem roottag 'Process_db') fehlen nach dem schreiben

'doc' kennt doch das roottag oder nicht?

habe mir noch mal getroot() genauer angesehen
aber eine lösung habe ich bis jetzt nicht gefunden

Code: Alles auswählen

import os
import time
from decimal import Decimal as deci
from xml.etree import ElementTree as etree

xmldoc = './xml/process.xml'

#read xmldoc
f = file(xmldoc, 'r')
s = f.read()
doc, id = etree.XMLID(s)
f.close()

#read path
path = id['1'].getchildren()[2].text

#start programm (with path) and detect runtime
start_time = time.time()
os.system(path)
runtime = time.time() - start_time

#runtime addition
old_runtime = id['1'].getchildren()[4].text 
new_runtime = deci(str(runtime)) + deci(old_runtime) 

#change RunTime element
id['1'].getchildren()[4].text = str(new_runtime)

#print
print 'ermittelte RunTime: ', runtime
print 'alte RunTime: ', old_runtime 
print 'neue RunTime: ', new_runtime

#write new_runtime
new_doc = etree.ElementTree(doc)
f = file(xmldoc, 'w')
new_doc.write(f)
f.close()
morgen verbaue ich alles schön sauber in funktionen und lese beim pythonbuch weiter ;)
BlackJack

MoR4euZ hat geschrieben:ein manko hat die sache allerdings noch :?

die ersten beiden zeilen in der xmldatei (über dem roottag 'Process_db') fehlen nach dem schreiben

'doc' kennt doch das roottag oder nicht?
`doc` ist an das Element, das den Wurzelknoten repräsentiert gebunden. Die XML Deklaration und die "processing instruction" liegen aber ausserhalb des Baums.

Die XML Deklaration schreibt `ElementTree` nur wenn eine andere Kodierung als ASCII oder UTF-8 gewählt wurde. Nur dann benötigt man sie unbedingt, sonst ist sie optional.

Ansonsten wirft `ElementTree` "processing instructions" und Kommentare beim einlesen weg. Da gibt's zwei Lösungen. Einmal eine mit `ElementTree` vom Author selbst: http://www.effbot.org/zone/element-pi.htm

Oder Du benutzt das `lxml` Paket. Das hat eine `ElementTree`-kompatible API und behält auch "processing instructions" und Kommentare im Baum. Die ausserhalb des Baums "überleben" aber anscheinend nur wenn man die `tostring()` Funktion auf Modulebene verwendet.
Antworten