XML-Problem: XML zu CSV

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.
PhantomWorks
User
Beiträge: 18
Registriert: Samstag 25. April 2009, 11:11

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!
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

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
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`.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

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")
    (...)
Zuletzt geändert von Dill am Mittwoch 20. Mai 2009, 11:39, insgesamt 1-mal geändert.
http://www.kinderpornos.info
BlackJack

@Dill: Mit XSLT kann aber schnell Probleme mit Feldinhalten bekommen, die den CSV-Trenner enthalten, oder?
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

wenn ich mit csv arbeite setzte ich eh '"' um alle zellen...
http://www.kinderpornos.info
BlackJack

@Dill: Das wäre bei Zahlen falsch und verschiebt das Problem auch nur, denn es könnte ja auch " innerhalb eines Feldes vorkommen.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

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.
http://www.kinderpornos.info
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Dill hat geschrieben:ja, aber die probleme hast du doch nicht nur mit xslt.
Aber nicht mit dem csv-Modul mit Python. :)
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

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.
http://www.kinderpornos.info
PhantomWorks
User
Beiträge: 18
Registriert: Samstag 25. April 2009, 11:11

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")
PhantomWorks
User
Beiträge: 18
Registriert: Samstag 25. April 2009, 11:11

EDIT: Habe den Fehler gefunden, jetzt erhalte ich die Liste:

Code: Alles auswählen

<Element Merkmalsliste at 2707fc0>
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? :?:
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. :roll:
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

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
http://www.kinderpornos.info
PhantomWorks
User
Beiträge: 18
Registriert: Samstag 25. April 2009, 11:11

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?
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.
PhantomWorks
User
Beiträge: 18
Registriert: Samstag 25. April 2009, 11:11

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?
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

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.
Zuletzt geändert von Dill am Freitag 22. Mai 2009, 11:24, insgesamt 1-mal geändert.
http://www.kinderpornos.info
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:

Code: Alles auswählen

except SomethingError, KeyError:
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:

Code: Alles auswählen

except (SomethingError, KeyError):
@Dill: Die Zeile 1984 ist in `lxml`, PhantonWorks' Programm hat in Zeile 50 das Problem.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

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"
http://www.kinderpornos.info
Antworten