Sax - xml Koordinaten in tags parsen

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
anton_82
User
Beiträge: 8
Registriert: Sonntag 17. Juli 2011, 11:49

Hallo, ich habe folgende Probleme
- ich bin noch Änfänger (das ändert sich hoffentlich mit er Zeit)
- ich möchte aus einer XML-datei (hier Dummydaten) die Koordinaten con bestimmten Haustypen herauslesen und woanders verwenden.
XML - Dummy:

Code: Alles auswählen

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="Beispieldummy.xsl"?>
<DATA xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Typ B.xsd">
	<Holzhaus>
		<besitzername>Lehmann</besitzername>
		<dmy>125</dmy>
		<Haus>
			<Bezeichnung>Haus_1</Bezeichnung>
			<Fenster>10</Fenster>
			<Eingang>
				<ebz>Nordseite</ebz>
				<ebzrechts>2526297,873</ebzrechts>
				<ebzhoch>5631284,145</ebzhoch>
			</Eingang>
			<Eingang>
				<ebz>Suedseite</ebz>
				<ebzrechts>2526297,813</ebzrechts>
				<ebzhoch>5631284,126</ebzhoch>
			</Eingang>
		</Haus>		
	</Holzhaus>
	<Steinhaus>
		<besitzername>Schmidt</besitzername>
		<dmy>19b</dmy>
		<Haus>
			<Bezeichnung>Haus_25</Bezeichnung>
			<Fenster>6</Fenster>
			<Eingang>
				<ebz>Nordseite</ebz>
				<ebzrechts>2526227,418</ebzrechts>
				<ebzhoch>5631274,965</ebzhoch>
			</Eingang>
			<Eingang>
			</Eingang>
			<Eingang>
			</Eingang>
		</Haus>		
	</Steinhaus>
</DATA>
zur Erklärung:
Es gibt Holzhäuser, die immer zwei Eingänge haben und Steinhäuser, die mindestens einen aber auch mehrere Eingänge haben können.
Ich brauche die Koordinaten (ebzrechts und ebzhoch) der Holzhäuser.

Code bislang:

Code: Alles auswählen

import sys
from xml.sax import make_parser, handler
import xml.sax as sax

class HausSaxHandler(handler.ContentHandler):
    
    def __init__(self):
        self.active = "" # aktuelles Tag
        
        self.p1ebz = ""
        self.p1ebzrechts = ""
        self.p1ebzhoch = ""
        
        self.p2ebz = ""
        self.p2ebzrechts = ""
        self.p2ebzhoch = ""
        self.m = ""       # Variable fuer eingang eins oder zwei
        self.parentNode = ""
        
    def startElement(self, name, attrs):
       
        self.active = name
  
        if name == "Holzhaus": 
            self.active = ""
            self.parentNode = "holzhaus"
            
            if name == "Eingang" and self.parentNode == "Holzhaus":
                self.active = ""
                
                self.p1ebz = ""       # Name Eingang 1
                self.p1ebzrechts = "" # Hochwert Eingang 1
                self.p1ebzhoch = ""   # Rechtswert Eingang 1  
                    
                self.p2ebz = ""       # Name Eingang 2  
                self.p2ebzrechts = "" # Hochwert Eingang 2 
                self.p2ebzhoch = ""   # Hochwert Engang 2
    
    def endElement(self, name):
        
            if self.parentNode == "holzhaus":
            
                if name == "Eingang" and self.m%2 == 0:
     
                    self.m +=1
                    print "anfang Punkt1 "
                    print self.besitzername
                    print self.p1ebzrechts
                    print self.p1ebzhoch
                    print "Ende Punkt1 \n"
                            
                elif name == "Eingang" and self.m%2 == 1: 
                                
                    self.m +=1
                    print "anfang Punkt 2 "
                    print self.p2ebzrechts
                    print self.p2ebzhoch
                    print "ende Punkt 2 \n"
  
    def characters(self, content):
        
        if self.active == "ebz" and self.m%2 == 0:
            self.p1ebz += content
        elif self.active == "ebzrechts" and self.m%2 == 0:
            self.p1ebzrechts += content
        elif self.active == "ebzhoch" and self.m%2 == 0:
            self.p1ebzhoch += content
       
        elif self.active == "ebz" and self.m%2 == 1:
            self.p2ebz += content
        elif self.active == "ebzrechts" and self.m%2 == 1:
            self.p2ebzrechts += content
        elif self.active == "ebzhoch" and self.m%2 == 1:
            self.p2ebzhoch += content
        
                         
parser = make_parser()
parser.setContentHandler(HausSaxHandler())
parser.parse(open('g:/dummy.xml', 'r'))

zum Code: ich habe mich sax entschieden, da meine xml-vorlage (wegen Datenschutz nicht gezeigt) schon aus 84.000 zeilen bei ca. 20Mio Zeichen besteht.
Der Gedanke ist, mit einer if-Abfrage die entsprechenden einträge auszuwählen.
Es gibt bestimmt eine schönere Möglichkeit das zu implementieren und dann auch zum gewünschten Ergebnis zu kommen.
Verzichte ich auf die

Code: Alles auswählen

if name == "Holzhaus": 
abfrage, erhalte ich, logisch, alle Koordinaten - auch nicht nicht benötigten

Momentan entscheide ich per modulo, ob es sich um Eingang eins oder zwei des Holzhauses handelt, und packe die in unterschieliche Variablen. Hier müssen verschiedene Variablen verwendet vverden, da zwichen den beiden Pubkte irgendwann mal eine Line gezeichnet werden soll (Das gehört aber nicht hierzu, es geht nur um die "Extraktion der Tags")
Steinhäuser sollen erstmal unberücksichtig bleiben.
wie kann ich dem parser denn mitteilen, dass er den TagInhalt der koordinaten nur dann ausliest, wenn es sich um ein "holzhaus" handelt.

Ich freue mich über ernstahfte Antworten und weiß, dass das mit eigener Arbeit verbunden ist.
Schon mal vielen Dank
Gruß,
Anton
Zuletzt geändert von anton_82 am Sonntag 17. Juli 2011, 18:11, insgesamt 1-mal geändert.
BlackJack

@anton_82: Ich habe noch so ein bisschen Probleme die eigentliche Frage aus dem ganzen heraus zu lesen. Das was Du da hast, tut ja schon etwas. Kommt dabei das gewünschte Ergebnis heraus? Wenn nein, was soll denn heraus kommen und was kommt stattdessen heraus?

Das mit `self.m` verstehe ich nicht so richtig. Warum zählst Du das immer rauf und nimmst es dann Modulo? Warum fängst Du nicht bei jedem Haus wieder bei 0 an. Beziehungsweise warum kannst Du die Eingangsdaten für ein Haus einfach in eine Liste für jedes Haus ablegen? Da kann man sich ganz einfach den 1., 2., … Eingang heraus holen, man braucht keine Zählvariable mehr, und es kommt mit jeder beliebigen Anzahl von Eingängen zurecht. Variablennamen die durchnummeriert sind, sollte man nach Möglichkeit durch Listen ersetzen, und nicht umgekehrt.

Wenn Du das `self.m` und den Modulo-Kram beibehalten möchtest, dann solltest Du vielleicht das Modulo da anwenden, wo `self.m` hochgezählt wird, so dass `self.m` tatsächlich immer nur 0 oder 1 ist. Dann wird der Code wo auf `self.m` getestet wird, einfacher.

Du machst mit den Ergebnissen hier ja nichts, ausser sie auszugeben. Was davon soll denn längerfristig gespeichert und weiter verarbeitet werden? Da wären vielleicht Klassen oder zumindest eine festgelegte, dokumentierte Datenstruktur ganz nützlich.

Das mit dem Holzhaus merkst Du Dir ja schon und fragst das auch ab. Deswegen verstehe ich die Frage danach wie man das macht nicht so richtig!? Ich würde das Attribut nicht `parentNode` sondern vielleicht `haustyp` oder so nennen, dann wird klarer was sich dahinter verbirgt. Ah, und da sehe ich vielleicht auch gerade ein Problem: Binde das nicht nur an 'holzhaus' wenn Du den Start von einem Holzhaus gefunden hast. Das bleibt dann natürlich für den Rest des Programms auf dem Wert, wenn Du das nicht am Ende eines Haus-Datensatzes zum Beispiel an `None` bindest, oder wenn ein Nicht-Holzhaus kommt. Am besten bindest Du da immer wenn ein Haustyp-Tag kommt, einfach dessen Tagnamen dran.

Zur SAX-Wahl: Hast Du denn tatsächlich Probleme mit der Datenmenge gehabt, oder ist das eine vorauseilende Optimierung? Mit der ElementTree-API und insbesondere mit `lxml` lässt sich XML nämlich in der Regel viel einfacher und bequemer verarbeiten als mit SAX.
anton_82
User
Beiträge: 8
Registriert: Sonntag 17. Juli 2011, 11:49

Hi,
@ BlackJack: Vielen Dank für die schnelle Antwort, bitte entschuldige meine fehlende Genauigkeit, ich übe des noch.
Binde das nicht nur an 'holzhaus' wenn Du den Start von einem Holzhaus gefunden hast. Das bleibt dann natürlich für den Rest des Programms auf dem Wert, wenn Du das nicht am Ende eines Haus-Datensatzes zum Beispiel an `None` bindest, oder wenn ein Nicht-Holzhaus kommt. Am besten bindest Du da immer wenn ein Haustyp-Tag kommt, einfach dessen Tagnamen dran.
...ist ein sehr guter Ansatz, auf dem ich jetzt erst weiter probieren.

Habe das dummyxml korrigiert, war noch fehlerbehaftet.
zum Modulo:
- hast recht, das mit self.m und Modulo ist unsinnig, wird geändert. (mache das dann mit 1 und 0 - damit der jeweils richtige Tag in die passende Variable gespeichert wird,
- für Punkt1(p1ebzrechts/p1ebzhoch) self.m = 0,
- für Punkt2((p1ebzrechts/p1ebzhoch) self.m = 0

zu den Listen/Printanweisung:
ich habe die Printanweisung nur als Platzhalter verwendet. Der Code ist für ein Plugin in unserem GISProgramm.
Dabei wird der Handler von einer anderen Klasse instanziert - der Pfad der XML Datei wird übergeben und statt der hier verwendeten Printanweisung an einen filewriter übergeben, der die Daten dann weiterverwendet, die Punkte 1 und 2 zu Linien verbindet.
Ich hatte die Idee mit der Liste auch - bin mir der Syntax unsicher. wie könnte sowas denn aussehen?

Frage:
Kann ich den ganzen Tag holzhaus denn in eine Liste packen? (das geht bestimmt, wie auch immer das formuliert werden könnte)
Mein Gedanke dabei ist: Was wenn ein Tag, z.B. <dmy> mal leer ist? Ich befürchte, dass sich dann die ganzen Positionen in der Liste verschieben (also dass z.B. <dmy> leer ist und sich dann Einträge nach oben verschieben und ich den falschen Schlüssel anspreche)

Sax verwendende ich, weil ich nicht weiß, wie das mit elementTree zu implemntieren ist. Wenn es da jemand ein gutes tutorial gibt, bitte bekannt geben. (Verwende hauptsächlich Galileo-Computing und wenige google-samples)

Die bislang gefunden Beispiele haben immer mit [attrs] gearbeitet - da weiß ich dann nicht, wie die synatx ohne diese Attribute geformt sein muss und ich den vorhandenen code umbasteln muss.
Für ein Sample oder Tutorial mit einer Listenfüllung aus XML bin ich durchaus offen. Meine Versuche endeten immer mit leeren Listen, habe es daher vorerst verworfen (Falls ich sie doch wieder aufnehme, gehe iach auch genauer drauf ein).

Listen würden mir auch besser gefallen, zumal dabei diverse Smells entfernt werden könnten. Mein Problem liegt darin, aus den korrekten Tag die Werte zu holen.

Soweit erstmal, ich versuch das mal umzucoden und gucke, was sich tut.


Danke und Gruß,
Anton
BlackJack

@anton_82: Bei der Listensyntax weiss ich jetzt nicht so genau was Du meinst. Literale Listen werden mit eckigen Klammern erstellt, aber das ist im Grunde ziemlich trivial und sollte in jedem Python-Tutorial klar werden. Du müsstest halt für jedes Haus am Anfang eine Liste für die Eingänge erstellen, und immer wenn ein Eingang fertig geparst ist, dessen Werte an die Liste anhängen. Und wenn das Haus dann fertig geparst ist, hast Du eine Liste mit den Daten für die Eingänge. Was man dann an eine Liste mit den Ergebnissen für alle gewünschten Häuser anhängen könnte.

Du müsstest Dir halt irgendeine Datenstruktur ausdenken, in der die Ergebnisse gespeichert und weitergeleitet werden. Also entweder eigene Klassen; die Grunddatentypen wie Listen, Tupel, Wörterbücher; `collections.namedtuple`; …; oder eine Mischung aus diesen Möglichkeiten.

Und Du musst diese Datenstruktur(en) auch wieder leeren, denn so wie es jetzt aussieht und Du ein Haus mit zwei Eingängen verarbeitest und danach eines mit einem Eingang, hast Du ja immer noch die Daten von dem zweiten Eingang des vorher verarbeiteten Hauses.

Wenn Du das Dokument komplett einliest und eine Baumstruktur aus Tag-Objekten hast, kannst Du die Objekte, die den Haus-Tags entsprechen, auch in eine Liste packen.

Falls es das umfassende Handbuch von Galileo ist, wird das hier nicht besonders empfohlen. Das Tutorial aus der Python-Dokumentation sollte man mal durchgearbeitet haben. Das bietet einen ganz guten Einstieg/Rundgang durch die Grundlagen was Kontrollstrukturen und Datentypen angeht.

Was Tutorials zur `ElementTree`-API angeht, findet man mit den Suchbegriffen ”elementtree tutorial” einiges im Netz. Unter anderem auch das Tutorial was in der Python-Dokumentation verlinkt ist, und das aus der Dokumentation von `lxml`.
anton_82
User
Beiträge: 8
Registriert: Sonntag 17. Juli 2011, 11:49

Sorry, in diesem Post war ein dicker Fehler.
anton_82
User
Beiträge: 8
Registriert: Sonntag 17. Juli 2011, 11:49

Hey BlackJack -
vielen Dank für den Tip, habes mit elementTree hinbekommen :D

Hab aber ein neues Problem, ist hier gepostet.
http://www.python-forum.de/viewtopic.ph ... er#p203923

Gruß,
Anton
Antworten