XML parsen mit ElementTree

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.
Antworten
pex
User
Beiträge: 9
Registriert: Samstag 23. September 2006, 01:17

Hallo Leutz...
Ich muss ein paar Daten hin und herschaufeln...das ganze habe ich mit ElementTree vor.
Und zwar liegt folgende XML Struktur vor:

Code: Alles auswählen

<Data>

   <head>
       <type>news_regional</type>
   </head>

   <body>
       <title>wetter</title>
       <image>
               <name>865_hdr_logo.gif</name>
               <image_server>localhost</image_server>
               <image_path>/repos/image/</image_path>
        </image>
        <content>schönes wetter</content>
    </body>

</Data>

Der Head Tag ist immer gleich, und muss nicht beachtet werden.
Der Body kann unterschiedliche Kinder beinhalten. Da ich nicht weiss welche Elemente in Body letztendlich stecken, komme ich mit find* nicht weit.

Als erstes soll nach allen image elemente gesucht werden. Und von denen dann die Kindelemente ausgelesen werden.

Als zweites dann alle Elemente mit Text die unter Body liegen (image tags ausgeschlossen). Dabei kann davon ausgegangen werden das die kinder von body keine weiteren kinder haben sondern nur einen text.

Am Schluss wäre ein Dictionary fein das alle Tags als Schlüssel und deren Texte als Wert enthält.


wäre über eure Hilfe sehr dankbar,

gruß
pex
BlackJack

pex hat geschrieben:Der Head Tag ist immer gleich, und muss nicht beachtet werden.
Der Body kann unterschiedliche Kinder beinhalten. Da ich nicht weiss welche Elemente in Body letztendlich stecken, komme ich mit find* nicht weit.

Als erstes soll nach allen image elemente gesucht werden. Und von denen dann die Kindelemente ausgelesen werden.
Hier würdest Du mit `find()` die `image`-Knoten finden können, aber da Du sowieso all Kinder vom `body`-Knoten verarbeiten willst, wäre es wohl effizienter die beiden Knotentypen `image` und nicht-`image` in einem Durchgang zu verarbeiten. Also einfach mit `getchildren()` über den `body`-Knoten gehen.

Code: Alles auswählen

from pprint import pprint
from elementtree import ElementTree as etree


def process_body(body_node):
    images = list()
    others = dict()
    for child_node in body_node.getchildren():
        if child_node.tag == 'image':
            image = dict((n.tag, n.text) for n in child_node.getchildren())
            images.append(image)
        else:
            others[child_node.tag] = child_node.text
    return images, others


def main():
    document = etree.parse('test.xml')
    body = document.find('body')
    pprint(process_body(body))
Ausgabe für Dein Beispieldokument:

Code: Alles auswählen

([{'image_path': '/repos/image/',
   'image_server': 'localhost',
   'name': '865_hdr_logo.gif'}],
 {'content': u'sch\xf6nes wetter', 'title': 'wetter'})
Du hattest über das gewünschte Zielformat der `image`-Knoten nichts gesagt, darum habe ich die erstmal auch in ein `dict` gesteckt.
pex
User
Beiträge: 9
Registriert: Samstag 23. September 2006, 01:17

wow...danke!! das ist es, hat einwandfrei funktioniert....

nur hab ich jetzt noch ein Problem mit den Zeichensätzen. So wie es aussieht ist das ein Problem mit denen viele nicht klar kommen. Habe mir einige Postings durchgelesen und auch die FAQ. Doch die ganze Sache ist mir immer noch nicht klar.

Also:

Folgendes Encoding habe ich überall verwendet:

Code: Alles auswählen

# -*- coding: utf-8 -*-
Ich speichere mittels ElementTree folgenden Text in eine Datei In der Datei steht folgendes:

Code: Alles auswählen

bär
Hier tippe ich auf Unicode

Die Datei wird mit Httprequest unter der httplib angefragt und und ausgegeben. Somit müsste der Text doch durch mein Encoding von Unicode in UTF-8 encodiert werden und korrekt auf dem bildschirm erscheinen....

Aber nein!

Wenn ich den direkt mit print ausgebe oder in eine Dateischreibe sieht der text so aus:

Code: Alles auswählen

"bär"
Hängt ich explizit ein encode daran:

Code: Alles auswählen

print text.encode('utf-8')
-> das gleiche =(

Sorry das ich nochmal ein Posting draufsetze über eine schon so oft angesprochene Thematik. Aber ich komme einfach nicht drauf...


Gruß
pey
[/quote]
BlackJack

Mit Deiner Beschreibung kann man nicht viel anfangen, weil nicht klar ist, wo 'bär' herkommt, d.h. wie es ursprünglich mal kodiert war/ist.

Du schreibst das Du den "Kodierungskommentar" mit 'utf-8' angibst. Frage: Stimmt das auch mit der Kodierung überein in der Du den Quelltext schreibst? Die müssen nämlich zusammenpassen.

Wenn Du in deinem Quelltext eine literale Zeichenkette schreibst, dann ist das streng genommen kein Text, also eine Folge von Zeichen, sondern eine Folge von Bytes. Welche Bytes da für 'bär' gespeichert werden, hängt von der Kodierung ab, die Dein Editor beim Speichern verwendet. Wenn Du eine Unicode-Zeichenkette schreibst, dann werden die Bytes in der Datei in der Kodierung interpretiert die im "Kodierungskommentar" angegeben ist. Da ist das zweite Zeichen von u'bär' also tatsächlich der Buchstabe 'ä'.
pex
User
Beiträge: 9
Registriert: Samstag 23. September 2006, 01:17

So...
ich habe das ganze mal genauer unter die Lupe genommen. Wie gesagt, das encoding im Quelltext und im Editor ist UTF-8.
Die Daten werden aus einer PostGreSQL Datenbank via psycopg2 eingelesen und als dictionary gespeichert.

wenn ich sie nun auf die Kommandozeile ausgebe:

Ausgabe des Dictionary wie es mit psycopg2 ausgelesen wird.

Code: Alles auswählen

print self.content
>>> ['fr\xc3\xb6hliche weihnachten', 'bl\xc3\xb6de encodings....',  news_regional']
Es scheint nicht wie ein Dictionary, ist es aber...ich bin die Ausgabe eines dict auch anderst gewöhnt.

Gebe ich danach ein Wert aus dem Dictionary aus:

Code: Alles auswählen

print self.content['title']
>>> fröhliche weihnachten

print self.content['content']
>>> blöde encodings....
Die Ausgabe ist korrekt! :?:

Da ich die Daten mit ElementTree in ein XML Baum verpacke und dann in eine Datei speichern:

Code: Alles auswählen

.
.
body = ET.SubElement(root, "body")
for key, att in self.content.items():
     obj = ET.SubElement(body, str(key))
     obj.text = str(att)

tree = ET.ElementTree(root)

tree.write(dateiname)  

Dateiinhalt:

Code: Alles auswählen

... <title>fröhliche weihnachten</title><content>blöde encodings....</content></body> ...
Schreibe ich die Dictionary Werte wiederum einzeln in eine Datei, werden die Umlaute sauber als ä und ö in die Datei geschrieben!!

Optional kann man der write Methode von eTree das encoding mitgeben:
write(file, encoding="us-ascii")
Ich hab gelesen das anderweitige Zeichen die mit ascci nicht darstellbar sind, als Unicode gespeichert werden (scheint mir in meinem Fall)

Wenn ich ein Encoding setze:

Code: Alles auswählen

]tree.write(dateiname, encoding="UTF-8")
steht folgendes in der Datei:

Code: Alles auswählen

<?xml version='1.0' encoding='UTF-8'?>
... <body><title>fröhliche weihnachten</title><content>blöde encodings....</content></body> ...
Das heißt eTree hat meine explizite Encoding Angabe zwar erkannt, scheint es aber nicht korrekt anzuwenden.

Nach ein bischen googlen habe ich gelesen das in versionen die write Methode von ElementTree ein explizites Encoding nicht richtig anwendet....

Was mich wundert, ist dass wenn ich anfangs das komplette Dictionary ausgebe (oben) die Umlaute ebenfalls nicht korrekt ausgegeben werden. Jedoch wenn nur ein Wert ausgebe die Umlaute passen.


über Hilfe wäre ich sehr dankbar.

Gruß
Rafael
BlackJack

pex hat geschrieben:Ausgabe des Dictionary wie es mit psycopg2 ausgelesen wird.

Code: Alles auswählen

print self.content
>>> ['fr\xc3\xb6hliche weihnachten', 'bl\xc3\xb6de encodings....',  news_regional']
Okay, das sieht so aus, als wenn die Daten in UTF-8 Kodierung vorliegen.
Dateiinhalt:

Code: Alles auswählen

... <title>fröhliche weihnachten</title><content>blöde encodings....</content></body> ...
So und hier haben wir wieder das Problem, dass diese Angabe nicht so hilfreich ist. Wie bist Du an diesen Dateiinhalt herangekommen? Wenn Du die Datei in einen Editor lädst oder auf der Konsole anzeigst, dann werden die *Bytes* in der Datei nach irgendeiner Kodierung in *Zeichen* umgewandelt. Ohne diese Kodierung zu kennen, kann man nicht sagen welche Bytes in der Datei stehen! So sieht zum Beispiel UTF-8 aus, wenn man die Daten als ISO-8859-1 dekodiert. In dem Fall wäre der Dateiinhalt also korrekt. Kann aber auch sein das die Daten als UTF-8 dekodiert wurden, dann steht in der Datei etwas falsches. Das kann man aber nur Anhand der Ausgabe da oben nicht erkennen.
Optional kann man der write Methode von eTree das encoding mitgeben:
write(file, encoding="us-ascii")
Ich hab gelesen das anderweitige Zeichen die mit ascci nicht darstellbar sind, als Unicode gespeichert werden (scheint mir in meinem Fall)
Als Unicode kann man nichts speichern! Es muss immer irgendwie kodiert werden wenn es das Programm verlässt.
Wenn ich ein Encoding setze:

Code: Alles auswählen

]tree.write(dateiname, encoding="UTF-8")
steht folgendes in der Datei:

Code: Alles auswählen

<?xml version='1.0' encoding='UTF-8'?>
... <body><title>fröhliche weihnachten</title><content>blöde encodings....</content></body> ...
Das heißt eTree hat meine explizite Encoding Angabe zwar erkannt, scheint es aber nicht korrekt anzuwenden.
Kodiert werden nur Unicode-Objekte. Bei Zeichenketten kann man die Kodierung gar nicht anwenden, weil ja nicht bekannt ist in welcher Kodierung die vorliegen. Und auch hier gilt wieder: Man kann von hier aus nicht sagen was *wirklich* in der Datei steht.
Was mich wundert, ist dass wenn ich anfangs das komplette Dictionary ausgebe (oben) die Umlaute ebenfalls nicht korrekt ausgegeben werden. Jedoch wenn nur ein Wert ausgebe die Umlaute passen.
Wenn Du Container ausgibst, dann wird die `repr()`-Form der Elemente ausgegeben und die Ausgabe ist korrekt. Das kannst Du mit einzelnen Elementen auch erzwingen: ``print repr(self.content[0])``. Diese Form ist die einzig sinnvolle Art sich die Daten zum debuggen anzuschauen weil man so erkennt welche Bytes *tatsächlich* in der Zeichenkette enthalten sind, ohne das irgendein Editor, eine Python-Shell oder eine Konsole die Daten nochmal dekodiert bevor sie angezeigt werden.
pex
User
Beiträge: 9
Registriert: Samstag 23. September 2006, 01:17

danke für die anwtort!

Wie wir also jetzt dank der repr() ausgabe wissen, liegt das ganze in UTF-8 vor.
Speichere ich das dann mit:

Code: Alles auswählen

tree.write(dateiname, encoding="UTF-8")
Öffne ich die Datei (dateiname) Eclipse 3.2.1 in dem encoding UTF-8:

Code: Alles auswählen

<body><title>fröhliche weihnachten</title><content>blöde encodings....</content>
Unter folgende den Encodings US-ASCII und ISO-8859-1 in Eclipse betrachtet verändert sich an der Datei gar nichts.

Schreibe ich beispielweise ein Wert aus dem Dict direkt in eine datei:

Code: Alles auswählen

body = ET.SubElement(root, "body")
    
        for key, att in self.content.items():
            obj = ET.SubElement(body, str(key))
            obj.text = str(att)
            print str(key), str(att)
            
            log = file('temp', "w")
            log.write(str(att))           ## !!!
            log.close()

       tree = ET.ElementTree(root)
       tree.write(dateiname, encoding="UTF-8")
Öffne ich es mit Eclipse in UTF-8:

Beispielsweise von content['content']

Code: Alles auswählen

blöde encodings....
ISO-8859-1

Code: Alles auswählen

blöde encodings....
Für mich sieht das aus als würde die write Methode von eTree irgendwas nicht korekt handhaben.

Ich weiss nicht wie ich mehr informationen rausgeben kann bzw. wie ich mit den tests weiter verfahren soll um ne lösung zu finden.
BlackJack

Ist das in Eclipse dann ein "normaler" Texteditor, oder schon einer der "XML aware" ist? Bei mir werden nämlich XML-charrefs für Zeichen ausserhalb von ASCII geschrieben:

Code: Alles auswählen

In [42]: a = etree.Element('root')

In [43]: a.text = 'hällo'  # Kodierung im Terminal ist UTF-8.

In [44]: doc = etree.ElementTree(a)

In [45]: doc.write(sys.stdout, encoding='utf-8')
<root>hällo</root>
Die Kodierung scheint dabei keine Rolle zu spielen.

Da XML keine Bytes kennt sondern nur Zeichen, sollte man alles ausserhalb von ASCII als Unicode-Objekte an `ElementTree` übergeben. Genauso handhabt `ElementTree` das in der anderen Richtung schliesslich auch.

Code: Alles auswählen

In [46]: a.text = 'hällo'.decode('utf-8')

In [47]: doc = etree.ElementTree(a)

In [48]: doc.write(sys.stdout)
<root>hällo</root>
Problem gelöst.
pex
User
Beiträge: 9
Registriert: Samstag 23. September 2006, 01:17

ok...soweit klar. Vielen Dank!! :D *überglücklich*
Antworten