Seite 1 von 2
XML-Problem: XML zu CSV
Verfasst: Mittwoch 20. Mai 2009, 10:18
von PhantomWorks
Hallo zusammen!
Ich habe ein kleines Problem, und zwar will ich aus bestimmten Tags einer größeren XML-Datei eine CSV Datei erstellen.
EinAusschnitt aus der XML:
Code: Alles auswählen
<Merkmal ID="34747">
<Name>Volumen</Name>
<AddData>
<Wert MerkmalID="Kurzbezeichnung">Volumen</Wert>
<Wert MerkmalID="Beschreibung">= Inhalt einer Verpackung</Wert>
</AddData>
</Merkmal>
Ich will nun die CSV daraus derart erzeugen, dass die Merkmal ID's in Spalte 1 Namen in Spalte 2, die Kurzbeschreibungen in Spalte 3 und die Beschreibungen in Spalte 4 stehen.
Wichtig ist vielleicht noch anzumerken, dass der oben stehende Ausschnitt noch 3 übergeordnete Hieraarchieebenen im Dokument besitzt.
Mein Problem ist einfach momentan, dass ich nicht weiß wie ich die einzelnen Tags ansprechen soll, da sie so ineinander verschachtelt sind.
Mein Baustelle:
Code: Alles auswählen
def _knoten_auslesen(knoten):
return eval("%s('%s')" % (knoten.getAttribute("BaseType")))
d = {}
baum = dom.parse("file.xml")
for eintrag in baum.firstChild.childNodes:
if eintrag.nodeName == "Merkmal":
schluessel = wert = None
for knoten in eintrag.childNodes:
if knoten.nodeName == "Merkmal ID":
schluessel = _knoten_auslesen(knoten)
elif knoten.nodeName == "Name":
wert1 = _knoten_auslesen(knoten)
elif knoten.NodeName =="Wert Merkmal ID":
wert2 = knoten_auslesen(knoten)
d[schluessel] = wert1, wert2
print d
Leider wird nur ein leeres Dictionary zurückgeliefert...
Danke für Eure Tipps!
Verfasst: Mittwoch 20. Mai 2009, 10:41
von helduel
Moin,
da mir persönlich DOM recht unsympatisch ist, würde ich dir einen Sax-Parser empfehlen. Richtig programmiert bekommt er auch keine Probleme bei richtig großen Datenmengen.
Gruß,
Manuel
Verfasst: Mittwoch 20. Mai 2009, 11:22
von BlackJack
@PhantomWorks: Bitte vergiss sofort wieder, dass es `eval()` gibt!
Und am besten auch den DOM/SAX-Kram. Für XML verwendet man in Python eher `ElementTree` (seit 2.5 in der Standardbibliothek) oder `lxml`.
Verfasst: Mittwoch 20. Mai 2009, 11:22
von Dill
SAX-parser ist hier nicht notwendig. (ausser das DOM der xml würde deinen arbeitsspeicher sprengen?)
eigentlich würde ich das mit xslt machen, wenn du öfter xml umformst wäre das einen blick wert.
falls nicht ist es am einfachsten sich die merkmal-tags per xpath zu holen:
(ich nutze hier lxml, finde ich die schönste xml-api für python
http://codespeak.net/lxml/xpathxslt.html)
Code: Alles auswählen
#gehe davon aus, dass du mit root eine ref auf die wurzel hast.
merkmale = root.xpath("//Merkmal") #liste mit allen merkmal-nodes
for m in merkmale :
name = m.xpath("./Name")
(...)
Verfasst: Mittwoch 20. Mai 2009, 11:38
von BlackJack
@Dill: Mit XSLT kann aber schnell Probleme mit Feldinhalten bekommen, die den CSV-Trenner enthalten, oder?
Verfasst: Mittwoch 20. Mai 2009, 11:42
von Dill
wenn ich mit csv arbeite setzte ich eh '"' um alle zellen...
Verfasst: Mittwoch 20. Mai 2009, 11:57
von BlackJack
@Dill: Das wäre bei Zahlen falsch und verschiebt das Problem auch nur, denn es könnte ja auch " innerhalb eines Feldes vorkommen.
Verfasst: Mittwoch 20. Mai 2009, 12:02
von Dill
ja, aber die probleme hast du doch nicht nur mit xslt.
oder meinst du dass das einfach komplizierter wird da rumzuschiffen?
bei zahlen lässt man die " natürlich weg, da stehen ja auch keine ; drin.
Verfasst: Mittwoch 20. Mai 2009, 12:10
von Rebecca
Dill hat geschrieben:ja, aber die probleme hast du doch nicht nur mit xslt.
Aber nicht mit dem csv-Modul mit Python.

Verfasst: Mittwoch 20. Mai 2009, 12:18
von Dill
an csv-writer hab ich jetzt garnicht gedacht. hab ich noch nie benutzt.
stimmt, das ist wahrscheinlich der einfachste und solideste weg.
ich nehme alles zurück und behaupte das gegenteil.
also lxml+xpath(oder elemtentree) und csv.writer würde ich dann bemühen.
Verfasst: Mittwoch 20. Mai 2009, 12:48
von PhantomWorks
Hallo!
Erst einmal danke für Eure Tipps. Ich bin momentan weder in dom noch in lxml richtig gut eingearbeitet. Von daher probiere ich momentan mit beiden Varianten herum.
@Dill: Wenn ich Deinen Code adaptiere liefert er mir lediglich eine leere Liste zurück. An was liegt das?
Code:
Code: Alles auswählen
from lxml import etree
doc = etree.parse("test.xml")
root = etree.Element("MerkmalsListe")
print root
attribute = root.xpath("//Merkmal")
print attribute
for m in attribute :
name = m.xpath("./Name")
AD = m.xpath ("./AddData")
for w in AD:
wert = w.xpath ("./Wert")
Verfasst: Mittwoch 20. Mai 2009, 13:13
von PhantomWorks
EDIT: Habe den Fehler gefunden, jetzt erhalte ich die Liste:
und die Liste der Merkmale sieht dann so aus:
Code: Alles auswählen
[<Element Attribute at 27030c0>, <Element Attribute at 27030f0>, <Element Attribute at 2703120>, <Element Attribute at 2703150>, <Element Attribute at 2703180>, usw usf]
Wenn ich mit
Code: Alles auswählen
count1=0
count2=0
count3=0
print attribute
for m in attribute :
name = m.xpath("./Name")
count1=count1+1
AD = m.xpath ("./AddData")
count2=count2+1
for w in AD:
wert = w.xpath ("./Wert")
count3=count3+1
print count1
print count2
print count3
durchzählen lasse, erscheint bei allen Countern die gleiche Zahl, was richtig ist, d.h. alle Elemente wurde erfasst und es gibt kein Merkmal das z.B. keinen Namen besitzt (leere Tags wurden im voraus entfernt)
Eigentlich garnicht so schwer...Zeit & Geduld braucht man eben.
Habt ihr nun bitte noch einen kleinen Tipp für mich, wie ich daraus nun die CSV erzeuge?

Verfasst: Mittwoch 20. Mai 2009, 13:15
von BlackJack
@PhantomWorks: Was hättest Du denn da jetzt erwartet? Du erzeugst einen Knoten "MerkmalsListe", das entspricht dem XML-Dokument '<MerkmalsListe />'. Wo soll denn da der Inhalt auf magische Weise herkommen? Vielleicht solltest Du einfach mal mit dem Dokument arbeiten, das Du geparst hast.

Verfasst: Mittwoch 20. Mai 2009, 13:34
von Dill
also wie blackjack schon sagte, so wird das nichts. (hast es aber schon fast)
schau dir mal die lxml-doku an. die ist kurz und gut.
in csv bekommst du das mit dem python csv-modul (csv.writer)
http://docs.python.org/library/csv.html#writer-objects
Verfasst: Freitag 22. Mai 2009, 10:28
von PhantomWorks
Hallo!
es funktioniert mittlerweile ganz gut und ich kann alle benötigten Elemente ansprechen.
Aus einer Fehlermeldung werde ich allerdings nicht schlau, da ich den KeyError per try/except ausgeschlossen habe:
Code:
Code: Alles auswählen
try:
BES = Wert.attrib ["Kurzbezeichnung"]
except KeyError:
BES = "Beschreibung nicht verfuegbar!"
Fehlermeldung:
Code: Alles auswählen
Traceback (most recent call last):
File "F:\...\versuch2.py", line 50, in <module>
BES = Wert.attrib ["Kurzbezeichnung"]
File "lxml.etree.pyx", line 1984, in lxml.etree._Attrib.__getitem__ (src/lxml/lxml.etree.c:45664)
KeyError: 'Kurzbezeichnung'
Was ist der Grund dafür?
Verfasst: Freitag 22. Mai 2009, 10:47
von BlackJack
Hm, Du führst eine andere Datei aus als Du denkst, oder die Zeile steht an anderer Stelle noch einmal ohne das ``try``/``except``, oder der Name `KeyError` ist an etwas anderes gebunden worden.
Wenn Du den Fehler gefunden hast, solltest Du Dir mal die `get()`-Methode auf `attrib` anschauen.
Verfasst: Freitag 22. Mai 2009, 11:12
von PhantomWorks
Also ich habe weiter oben im Code schonmal per except einen KeyError ausgeschlossen. Heißt das nun, dass der KeyError an das weiter oben ebenfalls in der for-Schleife stehende gebunden ist und weiter unten nicht nochmal verwendet werden kann? Wenn ja, wie kann ich dies umgehen?
Verfasst: Freitag 22. Mai 2009, 11:19
von Dill
nein, natürlich kannst du KeyError zweimal ausschliessen.
was du nicht machen darfst ist sowas:
Code: Alles auswählen
def KeyError: ...
#oder
class KeyError: ...
#oder
KeyError = irgendwas
#oder so
sicher, dass zeile 50 in deinem code der zeile 2 in dem geposteten snippet entspricht.
ich tippe mal darauf, dass du die datei zweimal da liegen hast und versehentlich die falsche ausführst.
Verfasst: Freitag 22. Mai 2009, 11:23
von BlackJack
@PhantomWorks: Du kannst `KeyError` so oft verwenden wie Du magst, nur muss der Name `KeyError` dann auch wirklich an das Objekt `exceptions.KeyError` gebunden sein.
Beispiel was nicht geht:
Code: Alles auswählen
KeyError = ValueError
try:
dict()[42]
except KeyError:
print 'boooo'
Denn das ``except`` behandelt jetzt keinen `KeyError` sondern einen `ValueError`.
Da Du den Quelltext der vorherigen Behandlung nicht gezeigt hast, bemühe ich mal die Glaskugel und sehe vor meinem inneren Auge eine Zeile die etwa so aussieht:
Richtig geraten? Damit wird *kein* `KeyError` behandelt und wenn ein `SomethingError` auftritt, wird der Name `KeyError` neu gebunden, nämlich an das `SomethingError`-Objekt was dort behandelt wird. Und schon ist ein `KeyError` kein `exceptions.KeyError` mehr.
Korrekt wäre:
@Dill: Die Zeile 1984 ist in `lxml`, PhantonWorks' Programm hat in Zeile 50 das Problem.
Verfasst: Freitag 22. Mai 2009, 11:41
von Dill
ich muss mal kurz abschweifen...
Code: Alles auswählen
try1_stmt ::= "try" ":" suite
("except" [expression [("as" | ",") target]] ":" suite)+
["else" ":" suite]
["finally" ":" suite]
kannst du mir mal erklären, was das für einen sinn macht statt dem "as" ein "," zu erlauben?
"except e1, e2" = "except e1 as e2"
was mir auch unklar ist: hier wird ja nur eine expression erlaubt, keine expressionlist. aber die wird ja akzeptiert. kann eine expr-list eine expr überall ersetzten? (steht nicht so in der grammatik). falls ja, wo kommt dann die forderung nach den klammern her?
"except (e1, e2)" = "except e1: suite; except e2: suite"