.xml Datei auswerten - Problem.

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
GameChanger
User
Beiträge: 14
Registriert: Dienstag 21. Februar 2017, 15:14

Hey Leute,
Ich habe Probleme mit der Auswertung einer XML Datei. Ich werde mein Problem sehr detailliert beschreiben, da ich ein nicht allzu erfahrener Programmierer bin, und mir nicht sicher bin, ob ich, mit meiner Herangehensweise, überhaupt das erreichen kann, was ich mir vorstelle.

ich habe eine .xml Datei folgender Struktur:

Code: Alles auswählen

	<RE-RT>...
			<NAME>...</NAME>
						<Fehler>...</Fehler>
						<Fehler>...</Fehler>
						<Fehler>...</Fehler>
						<Fehler>...</Fehler>
		</RE-RT>
		<RE-RT>...
			<NAME>...</NAME>
						<Fehler>...</Fehler>
						<Fehler>...</Fehler>
						<Fehler>...</Fehler>
						
		</RE-RT>
		<RE-RT>...
			<NAME>...</NAME>
						<Fehler>...</Fehler>
						<Fehler>...</Fehler>
						<Fehler>...</Fehler>
						<Fehler>...</Fehler>
						<Fehler>...</Fehler>
						<Fehler>...</Fehler>
		</RE-RT>
		....
Die Datei ist, wie könnte es anders sein, ~10000 Zeilen lang.
Mein Ziel ist es, zumindest vorerst, die Fehler auszulesen, jedoch mit dem zugehörigen Namen.

Bis jetzt bin ich soweit, dass mir die Fehler einzeln ausgegeben werden.

Code: Alles auswählen

from xml.dom import minidom
 
xmldoc = minidom.parse("xmldoc.xml")

rert = xmldoc.getElementsByTagName("RE-RT")

for nombre in rert:
	bezeichnung = nombre.getElementsByTagName("NAME")[0]
	print (bezeichnung.firstChild.data)
Mein Bestreben war dann im Anschluss, dass ich die einzelnen Fehler gemeinsam mit der zugehörigen Bezeichnung auswerte.

Mein Ansatz wäre dieser gewesen:

Code: Alles auswählen

for nombre in rert:
    			bezeichnung = nombre.getElementsByTagName("NAME")[0]
 
   			 for malus in rert:
       						 	Fehler = malus.getElementsByTagName("Fehler")[0]
        						print (bezeichnung.firstChild.data, Fehler.firstChild.data)
Also meine gewünschte Ausgabe wäre:
Name1: Fehler1
Name1: Fehler2
Name2: Fehler4
Name2: Fehler12
Name2: Fehler34
....

Dies kommt aber lediglich auf die Meldung "for label in rert: TypeError: 'Element' object is not iterable"

Zudem:

Wenn kein Fehler nicht auftritt, steht

Code: Alles auswählen

<Fehler>Unused</Fehler>
Das würde ich gerne bei der Ausgabe auslassen! (gibts es da eine Möglichkeit?)

Ich habe es mit

Code: Alles auswählen

	 l={"unused"}
 					Fehler not in l
als Bedingung probiert, hatte aber leider keinen Erfolg.

Zu guter letzt würde mich interessieren, wie ich mir die ausgegebene Daten in eine Liste oder Array speichern lassen kann, um die dann in eine Excel oder .txt. Datei zu exportieren. Mit den Minidom Klassen geht es ja nicht, diese direkt in eine Datei ausgeben zu lassen, oder?!

Würde mich sehr freuen, wenn ihr mich erhellt.
Zuletzt geändert von Anonymous am Dienstag 28. Februar 2017, 15:15, insgesamt 2-mal geändert.
Grund: Quelltext in Codebox-Tags gesetzt.
BlackJack

@GameChanger: `nombre` und `malus` sind falsche Namen. `nombre` weil es wirklich verwirrend ist <RE-RT>-Elemente `nombre` zu nennen und dann darin <NAME>-Elemente zu suchen und die *anders* zu nennen.

Den Quelltext zur ``for label in rert: TypeError: 'Element' object is not iterable``-Ausnahme zeigst Du gar nicht.

Die Einrückung ist hier Forum sehr durcheinander geraten. Man kann hier zwar noch ganz gut raten wie das gemeint ist, aber korrekt eingerückt wäre besser.
Obwohl es bei ``for malus in rert:`` nicht wirklich deutlich ist, denn das macht so weder als verschachtelte Schleife noch auf der gleichen Ebene wie die andere Schleife wirklich Sinn.

Als erstes würde ich mir an Deiner Stelle die `ElementTree`-API für XML anschauen. Minidom benutzt keiner (mehr).

Der Umgang mit Listen sollte in jedem Einsteigertutorial erklärt werden. Das sind absolute Grundlagen. In der Python-Dokumentation befindet sich ein Tutorial.

Um Excel-Dateien zu erstellen gibt es externe Module die man sich installieren kann. `openpyxl` zum Beispiel. Für CSV-Dateien gibt es das `csv`-Modul in der Standardbibliothek.
GameChanger
User
Beiträge: 14
Registriert: Dienstag 21. Februar 2017, 15:14

Hey,

danke für die rasche Antwort.

Okay, danke mal für die Tipps. Bin jetzt eh schon ne Weile beim Element Tree, scheint mir vernünftiger.

Bis zu nächsten Frage ;)
GameChanger
User
Beiträge: 14
Registriert: Dienstag 21. Februar 2017, 15:14

BlackJack hat geschrieben:
Den Quelltext zur ``for label in rert: TypeError: 'Element' object is not iterable``-Ausnahme zeigst Du gar nicht.
Doch, das soll der hier sein:

Code: Alles auswählen

for nombre in rert:
                    bezeichnung = nombre.getElementsByTagName("NAME")[0]
     
                 for malus in rert:
                                    Fehler = malus.getElementsByTagName("Fehler")[0]
                                    print (bezeichnung.firstChild.data, Fehler.firstChild.data)
BlackJack

@GameChanger: Das soll er vielleicht sein, ist er aber nicht. Oder die Fehlermeldung ist falsch, denn in der kommt der Name `label` vor, im Quelltext aber nicht.
GameChanger
User
Beiträge: 14
Registriert: Dienstag 21. Februar 2017, 15:14

BlackJack hat geschrieben:@GameChanger: Das soll er vielleicht sein, ist er aber nicht. Oder die Fehlermeldung ist falsch, denn in der kommt der Name `label` vor, im Quelltext aber nicht.


Ja, im Original sind die Variablen anders benannt, jedoch ist der Aufbau gleich, aber wie dem auch sei.
Mit Elementtree bin ich jetzt soweit, wie ich mit Minidom auch schon war.

Mein zentrales Problem ist der Aufbau des .xmls.

Code: Alles auswählen

        
 <tag1>
 	<tag2>
 		<tag3>
      			  <RE-RT>...
               			 <NAME>name1</NAME>
             				   <fehlerliste>
                           				 <Fehler>F1</Fehler>
                          				 <Fehler>F2</Fehler>
                            				<Fehler>F3</Fehler>
                           				 <Fehler>Unused</Fehler>
                			 </fehlerliste>
                 
          		  </RE-RT>
          		    <RE-RT>...
               			 <NAME>name2</NAME>
             				   <fehlerliste>
                           				 <Fehler>F4</Fehler>
                          				 <Fehler>Unused</Fehler>
                            				<Fehler>F5</Fehler>
                           				 <Fehler>Unused</Fehler>
                			 </fehlerliste>
                 
          		  </RE-RT>
          		    <RE-RT>...
               			 <NAME>name3</NAME>
             				   <fehlerliste>
                           				 <Fehler>F6</Fehler>
                          				 <Fehler>F7</Fehler>
                            				<Fehler>F8</Fehler>
                           				 <Fehler>Unused</Fehler>
                			 </fehlerliste>
                 
          		  </RE-RT>
       			 </tag3>
 		</tag2>
 	</tag1>
Meine gewünschte Ausgabe wäre jetzt:

Name1: F1
Name1: F2
Name1: F3
Name2: F4...etc.


Durch die Struktur der Datei, sind die gängigen Tutorials zu verwerfen, da die die .xmls. stets besser gegliedert haben.
Jetz ist meine Frage, hat jmd eine Tipp für mich, bezgl eines "Advanced" tutorials bzw. kann ich mit Elementtree überhaupt eine solche Ausgabe erreichen?

Oder hat jmd. generell eine Idee für das Problem?
Zuletzt geändert von Anonymous am Dienstag 28. Februar 2017, 17:45, insgesamt 1-mal geändert.
Grund: Quelltext in Codebox-Tags gesetzt.
BlackJack

@GameChanger: Ich sehe das Problem nicht und auch nicht wofür man ein extra Tutorial bräuchte. Man muss in einem Element nach einem bestimmten Subelement und nach vielen mit dem gleichen Tag suchen können, und auf den Text eines Elements zugreifen können. Mehr braucht man hier nicht was XML-spezifisch wäre.

Teil Dein Problem auf Teilprobleme auf, und die gegebenenfalls wieder, solange bis sich die Teilprobleme mit wenigen Zeilen Code lösen lassen. Für den Anfang lieber zu kleine Funktionen als zu grosse. Überleg Dir was so eine Funktion als Eingabe bekommt und wie der Rückgabewert aussehen muss. Schreib das nicht als einen grossen Codehaufen auf Modulebene runter.

Übliche ”Sollbruchstellen” sind beispielsweise Eingabe/Verarbeitung/Ausgabe, wobei der Verarbeitung bei so einer einfachen Konvertierung oft weg fällt wenn die Eingabe einen Rückgabewert hat mit dem die Ausgabe direkt etwas anfangen kann.

Dann bieten sich oft Funktionen an wenn man etwas wiederholt für gleiche Datenstrukturen machen muss. Also zum Beispiel ist das einlesen aus der XML-Datei ja das Verarbeiten jedes <RE-RT>-Elements. Und das Verarbeiten eines <RE-RT>-Elements hat als Teilaufgabe das Verarbeiten jedes <Fehler>-Elements in der <fehlerliste>. Da kommen wahrscheinlich ”zu kleine” Funktionen am Ende heraus, insbesondere wenn man Sprachmittel wie „list comprehensions“ verwendet, aber es ist einfacher die dann hinterher wenn es funktioniert zu „inlinen“, als sich vorher mit zu viel Code herum zu schlagen, dessen Einzelteile nicht das machen was sie sollen.
GameChanger
User
Beiträge: 14
Registriert: Dienstag 21. Februar 2017, 15:14

Jetzt bin ich zurrück mit einem kleineren Problem:

mein XML:

Code: Alles auswählen

...
-<DSM-DFC>

	<SHORT-NAME>DFC_BusDiagOpenLoad_CanLP</SHORT-NAME>

	<DSM-DFC-INHS>

		<DSM-DFC-INH>FId_BusDiag_stFadeOutLP</DSM-DFC-INH>

		<DSM-DFC-INH>FId_MoFVarST_VRS_GRB</DSM-DFC-INH>

		<DSM-DFC-INH>FId_Unused</DSM-DFC-INH>

		<DSM-DFC-INH>FId_Unused</DSM-DFC-INH>

		<DSM-DFC-INH>FId_Unused</DSM-DFC-INH>

	</DSM-DFC-INHS>
...
</DSM-DFC>
hier mein bisheriger code:

Code: Alles auswählen

import xml.etree.ElementTree as ET
tree = ET.parse("C:\\...\Label Abgleich Test\dsm_export.xml")
root = tree.getroot()


dfcs = root.iter("DSM-DFC")

for dfcs_name_inhs in dfcs:
    name = dfcs_name_inhs.findtext("SHORT-NAME")
    if name == "DFC_BusDiagOpenLoad_CanLP":
        liste_fids = dfcs_name_inhs.iter("DSM-DFC-INHS")
        for names in liste_fids:
            blub = names.findall("DSM-DFC-INH")
            
            print(name,blub)
Damit bekomme ich den "Short Name" ausgegeben, und die Liste der dazugehörigen "FId"s nur leider nicht den Text im FId Tag, siehe hier:

Code: Alles auswählen

 DFC_BusDiagOpenLoad_CanLP [<Element 'DSM-DFC-INH' at 0x000000000398FDB8>, <Element 'DSM-DFC-INH' at 0x000000000398FE58>, <Element 'DSM-DFC-INH' at 0x000000000398FEF8>, <Element 'DSM-DFC-INH' at 0x000000000398FF98>, <Element 'DSM-DFC-INH' at 0x0000000003991098>]
Anmerkung: ich habe natürlich auch "findtext" probiert, nur da wird mir ja nur das erste element in der Liste ausgegeben.

Natürlich hab ich das dann auch mit "itertext()" probiert:

Code: Alles auswählen

import xml.etree.ElementTree as ET
tree = ET.parse("C:\\...\Label Abgleich Test\dsm_export.xml")
root = tree.getroot()

fid = ET.Element("DSM-DFC-INHS")
name = fid.itertext()
print(name)
Ausgabe:

Code: Alles auswählen

<_elementtree._element_iterator object at 0x0000000003860048>
Wer eine Idee???
Zuletzt geändert von Anonymous am Mittwoch 1. März 2017, 15:19, insgesamt 1-mal geändert.
Grund: Quelltext in Codebox-Tags gesetzt.
BlackJack

@GameChanger: Du bekommst im ersten Fall halt die Elemente. Von denen müsstest Du dann nur noch den Text abfragen. Also von jedem Einzeln → Schleife. Oder „list comprehension“.

Im zweiten Fall müsstest Du über den Iterator halt auch noch *iterieren*. Also mit einer Schleife. Oder einer „list comprehension“. Beziehungsweise könnte hier auch einfach die `list()`-Funktion reichen.
GameChanger
User
Beiträge: 14
Registriert: Dienstag 21. Februar 2017, 15:14

okay, danke, aber:

Code: Alles auswählen

dfcs = root.iter("DSM-DFC")

for dfcs_name_inhs in dfcs:
    name = dfcs_name_inhs.findtext("SHORT-NAME")
    if name == "DFC_BusDiagOpenLoad_CanLP":
        liste_fids = dfcs_name_inhs.iter("DSM-DFC-INHS")
        for names in liste_fids:
            blub = names.findall("DSM-DFC-INH")
            for text in blub:
                ende = text.findtext("DSM-DFC-INH")
                print(name,ende)
       
gibt diese Ausgabe:

Code: Alles auswählen

DFC_BusDiagOpenLoad_CanLP None
DFC_BusDiagOpenLoad_CanLP None
DFC_BusDiagOpenLoad_CanLP None
DFC_BusDiagOpenLoad_CanLP None
DFC_BusDiagOpenLoad_CanLP None
BlackJack

@GameChanger: Was Du da an den Namen `text` bindest sind <DSM-DFC-INH>-Elemente und Du suchst *darin* noch mal nach <DSM-DFC-INH>-Elemente.  Da sind aber keine.
GameChanger
User
Beiträge: 14
Registriert: Dienstag 21. Februar 2017, 15:14

BlackJack hat geschrieben:eChanger: Was Du da an den Namen `text` bindest sind <DSM-DFC-INH>-Elemente und Du suchst *darin* noch mal nach <DSM-DFC-INH>-Elemente. Da sind aber keine.
okay, ja aber genau, das ist mein Problem, ich weiß eben nicht wie ich nach diesem Text suchen soll, da ich zum Beispiel mittels ...findtext("SHORT-NAME") ja sehr wohl die Bezeichnung finde.

Und mir ist klar, dass die Sache hier etwas anders gelagert ist, und genau das ist der Grund, weshalb ich nicht mehr weiter weiß...und hier um Rat frage :)
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@GameChanger: Du hast die Elemente schon gesucht, Du mußt nur noch den Text **abfragen**:

Code: Alles auswählen

blub = names.findall("DSM-DFC-INH")
for element in blub:
    print(name, element.text)
GameChanger
User
Beiträge: 14
Registriert: Dienstag 21. Februar 2017, 15:14

Sirius3 hat geschrieben:@GameChanger: Du hast die Elemente schon gesucht, Du mußt nur noch den Text **abfragen**:

Code: Alles auswählen

blub = names.findall("DSM-DFC-INH")
for element in blub:
    print(name, element.text)

DANKE! :)...das mit der "element" Abfrage...top!
Antworten