Problem mit lxml

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.
lunar

Montag 3. November 2008, 23:32

Ich muss die Bedeutung nicht nachsehen. Auch würde ich bei jemandem, der sich mit XML beschäftigt, eher XPath Kenntnisse erwarten, insofern ist der Einsatz von XPath letztlich sogar lesbarer, wenn der Code auch von Nicht-Webentwicklern gelesen wird.

Außerdem ist CSS verglichen mit XPath doch eher limitiert. Mit XPath kann man z.B. komfortabel Textknoten extrahieren, was mit CSS nicht möglich ist. Außerdem kann man mit XPath auf sichere Weise dynamische Ausdrücke erstellen, da XPath Variablen kennt, was bei CSS ebenfalls nicht der Fall ist. Da wird ein Anführungszeichen im Namen einer Person mitunter zum Problem ;)
blubber
User
Beiträge: 123
Registriert: Montag 19. März 2007, 09:08

Dienstag 4. November 2008, 07:35

Hallo zusammen,

erstmal danke, dass ihr versucht mir zu helfen und mein "Unverständnis" zu beseitigen.
Die angegebene Lösung

Code: Alles auswählen

root.xpath('//daten[@Vorname="hans"]')[0].attrib['Vorname'] = 'egon'
funktioniert zwar, aber ist jetzt nicht wirklich eindeutig oder?
Also es können natürlich mehrere "Hans" vorhanden sein, mit teils unterschiedlichen aber teils sogar gleichen Nachnamen.
Eine absolute Eindeutigkeit, welcher Vorname und Nachname nun wirklich gemeint ist, erreicht man nur über den kompletten Pfad. Daher gab ich an, dass alles bekannt ist (und meiner Meinung auch sein muss).
Sprich, die Lösung muss auch folgende Informationen beinhalten:

Gesamt zeit = heute
test name = Test1
datensatz ID = 5
Vorname = hans

Nur dann wird wirklich der "hans" getroffen, der gemeint ist oder? Vielleicht könnt ihr mir diesbezüglich nochmals helfen...

Grüße
blubber
User
Beiträge: 123
Registriert: Montag 19. März 2007, 09:08

Dienstag 4. November 2008, 07:40

Ok, ich glaub sogar, ich habs n bissl gecheckt und diese Lösung hier gefunden:

Code: Alles auswählen

from lxml import etree as ET 

root = ET.fromstring("""<Gesamt zeit = "heute"> 
    <test name = "Test1"> 
       <datensatz ID = "1"> 
           <daten 
                  Vorname="otto" 
                  Nachname = "mustermann"/> 
       </datensatz>
       <datensatz ID = "4"> 
           <daten 
                  Vorname="hans" 
                  Nachname = "stachelmann"/> 
       </datensatz>        
       <datensatz ID = "5"> 
           <daten 
                  Vorname="hans" 
                  Nachname = "lummelmann"/> 
       </datensatz>
    </test> 
    <test name = "Test2"> 
    </test> 
    <test name = "Test3"> 
    </test> 
</Gesamt> """) 

root.xpath('/Gesamt[@zeit="heute"]/test[@name="Test1"]/datensatz[@ID="4"]/daten')[0].attrib['Vorname'] = 'Manni'
print ET.tostring(root)
blubber
User
Beiträge: 123
Registriert: Montag 19. März 2007, 09:08

Dienstag 4. November 2008, 10:38

Bin noch auf zwei weitere, kleine Probleme gestoßen, die mir grad Kopfzerbrechen bereiten. Wie ich festgestellt habe, kann meine XML pro Datensatz mehrere Daten enthalten, also so:

Code: Alles auswählen

from lxml import etree as ET 

root = ET.fromstring("""<Gesamt zeit = "heute"> 
    <test name = "Test1"> 
       <datensatz ID = "1"> 
           <daten 
                  Vorname="otto" 
                  Nachname = "waldmann"/> 
       </datensatz>
       <datensatz ID = "4"> 
           <daten 
                  Vorname="hans" 
                  Nachname = "stachelmann"/> 
           <daten 
                  Vorname="hans" 
                  Nachname = "mustermann"/> 
       </datensatz>        
    </test> 
    <test name = "Test2"> 
    </test> 
    <test name = "Test3"> 
    </test> 
</Gesamt> """) 

root.xpath('/Gesamt[@zeit="heute"]/test[@name="Test1"]/datensatz[@ID="4"]/daten')[0].attrib['Vorname'] = 'Manni'
print ET.tostring(root)
Problem1: Dieses [0] am Ende der XPath Anweisung bedeutet ja, dass er den Vornamen von "hans stachelmann" in "Manni" ändern würde, also das 0-te Element. Nun ist es aber so, dass mir nicht die Nummer bekannt ist, sondern eben alle Attribute.
Also ich weis, ich möchte den Vornamen "hans" ändern, welcher den Nachnamen "stachelmann" besitzt. Kann man diese Bedingung irgendwie einbauen und stattdessen quasi auf die Nummerierung verzichten?

Problem2: Ich hab die ganzen Informationen natürlich in Variablen stehen. Also zum Beispiel bei

Code: Alles auswählen

.../Gesamt[@zeit="heute"]/...
steht das "heute" in der Variable Zeitpunkt.
Wenn ich nun aber

Code: Alles auswählen

Zeitpunkt = "heute"
.../Gesamt[@zeit=Zeitpunkt]/...
schreibe, dann bekomm ich einen Fehler. List index out of range.
Wie ist die Syntax, um Variablen anstatt direkt Strings nehmen zu können?


Grüße
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dienstag 4. November 2008, 10:46

blubber hat geschrieben:Wie ist die Syntax, um Variablen anstatt direkt Strings nehmen zu können?
Das sind ganz normale Strings, also so wie man das in Strings immer machen kann

Code: Alles auswählen

zeitpunkt = "heute"
print ".../Gesamt[@zeit=%(zeitpunkt)]/..." % {'zeitpunkt' : zeitpunkt}
Zu deiner anderen Frage, es geht und ist trivial. Keine Ahnung warum du das nicht einfach ausprobiert hast:

Code: Alles auswählen

root.xpath('/Gesamt[@zeit="heute"]/test[@name="Test1"]/datensatz[@ID="4"]/daten[@Nachname="stachelmann"]')[0].attrib['Vorname'] = 'Manni'
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Dienstag 4. November 2008, 11:04

@blubber: Du musst sauberer zwischen Python und XPath unterscheiden. Das ``[0]`` ist nicht im XPath-Ausdruck sondern im Python-Quelltext. Bei XPath-Ausdrücken, die Knotenmengen beschreiben, liefert die `xpath()`-Methode immer eine Liste zurück und auf die bezieht sich der Indexzugriff.

Ebenso kannst Du in XPath-Ausdrücken nicht einfach Python-Namen verwenden und erwarten, das die da auf magische Weise bekannt sind.

Zum ersten Problem: Dann schränk doch einfach den Datenknoten noch mit der Bedingung zum Nachnamen ein.

Zum zweiten Problem: Vergiss was Leonidas dazu geschrieben hat, das ist nämlich nicht viel besser als Variablen per Zeichenformatierung in SQL ein zu fügen. :-)

Und dann lese ich Dir hier mal die Doku vor:
http://codespeak.net/lxml/xpathxslt.html hat geschrieben:The xpath() method has support for XPath variables:

Code: Alles auswählen

>>> expr = "//*[local-name() = $name]"

>>> print(root.xpath(expr, name = "foo")[0].tag)
foo

>>> print(root.xpath(expr, name = "bar")[0].tag)
bar

>>> print(root.xpath("$text", text = "Hello World!"))
Hello World!
blubber
User
Beiträge: 123
Registriert: Montag 19. März 2007, 09:08

Dienstag 4. November 2008, 11:30

Zum ersten Problem: Ok, mein Fehler. Selbstverständlich habe ich das ausprobiert. Mein Fehler war einfach, dass ich mit meiner Ziel-XML rumprobiert habe, und die ist relativ groß/komplex. Daher hat es funktioniert und ich habe es einfach nur übersehen :(

Zum zweiten Problem: Ich kenn den Ausschnitt der Docu, hab sie - wie erwähnt - wirklich durchgelesen, nur hilft mir das irgendwie nicht weiter :cry:
Hab rumprobiert mit

Code: Alles auswählen

Zeitpunkt = "heute"
.../Gesamt[@zeit=$Zeitpunkt]/...
und lauter so Sachen, aber mir wird aus dem Codebeispiel der Doku nicht ersichtlich, wie ich das jetzt machen muss.
Bin glaub echt zu blöd für sowas :cry:
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dienstag 4. November 2008, 11:46

Funktioniert hier problemlos:

Code: Alles auswählen

root.xpath('/Gesamt[@zeit=$zeitpunkt]/test[@name="Test1"]/datensatz[@ID="4"]/daten[@Nachname=$nachname]', zeitpunkt="heute", nachname="Stachelmann")[0].attrib['Vorname'] = 'Manni'
Nehme ich richtig an dass die XPath Variablen richtig escaped werden, bevor sie eingefügt werden? Dann wäre das tatsächlich sinnvoller als String-Formatting (was in meinem Beispiel im übrigen sowieso nicht funktioniert hätte). Soweit ich das sehe, ja, aber die Dokumentation dazu ist etwas dünn.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
blubber
User
Beiträge: 123
Registriert: Montag 19. März 2007, 09:08

Dienstag 4. November 2008, 11:51

ok, so funktioniert das bei mir jetzt auch.
Danke :oops: :(
Benutzeravatar
cofi
Moderator
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Dienstag 4. November 2008, 11:55

blubber hat geschrieben: Hab rumprobiert mit

Code: Alles auswählen

Zeitpunkt = "heute"
.../Gesamt[@zeit=$Zeitpunkt]/...
und lauter so Sachen, aber mir wird aus dem Codebeispiel der Doku nicht ersichtlich, wie ich das jetzt machen muss.
Und warum hast du es nicht mit ("../Gesamt[@zeit=$Zeitpunkt]/.../", Zeitpunt = "jetze")

ausprobiert? Immerhin fliegt das seit zahlreichen Posts im Thread rum ;)
blubber
User
Beiträge: 123
Registriert: Montag 19. März 2007, 09:08

Dienstag 4. November 2008, 13:02

Ja es sieht vielleicht so aus, als probier ich nix, aber ich probier so viel herum, dass ich schon garnichtmehr weis, was ich alles probiert und nicht probiert habe. Genau so mit dem Problem1 von eben, was eigentlich funktioniert hatte und ich es garnicht mehr gemerkt habe.
Also danke nochmal für die Geduld. Mein Problem konnte ich nun lösen.
Nurnoch rein aus Interesse und nicht mehr notwendig:
(Falls ich nerve, kann das Thema hier auch geschlossen werden)

Wäre es, falls nötig, denn auch möglich, den "Durchlauf" der XML zu kombinieren? Als Beispiel wieder der Ausgangscode:

Code: Alles auswählen

from lxml import etree as ET 

root = ET.fromstring("""<Gesamt zeit = "heute"> 
    <test name = "Test1"> 
       <datensatz ID = "1"> 
           <daten 
                  Vorname="otto" 
                  Nachname = "waldmann"/> 
       </datensatz>
       <datensatz ID = "4"> 
           <daten 
                  Vorname="hans" 
                  Nachname = "stachelmann"/> 
           <daten 
                  Vorname="hans" 
                  Nachname = "mustermann"/> 
       </datensatz>        
    </test> 
    <test name = "Test2"> 
    </test> 
    <test name = "Test3"> 
    </test> 
</Gesamt> """) 

root.xpath('/Gesamt[@zeit="heute"]/test[@name="Test1"]/datensatz[@ID="4"]/daten[@Nachname="mustermann"]')[0].attrib['Vorname'] = 'Manni'
print ET.tostring(root)
Wenn man zum Beispiel möchte, dass man die @zeit konkret angibt, @test konkret angibt, nun aber datensatz nicht konkret (über @ID=4) sondern als Nummer angibt.....also zweiter Datensatz oder so.
Im Gesamten (Pseudo) in der Art:

Code: Alles auswählen

root.xpath('/Gesamt[@zeit="heute"]/test[@name="Test1"]/datensatz[Nr2]/daten[@Nachname="mustermann"]')[0].attrib['Vorname'] = 'Manni'
Wäre das '''datensatz[Nr2]''' irgendwie möglich?
Wie gesagt, ist für mein Problem nicht mehr notwendig, wäre nurnoch Interesse.

Danke an alle.

Gruß
BlackJack

Dienstag 4. November 2008, 13:38

@blubber: Ist möglich: '.../datensatz[2]/...'.
Antworten