Seite 2 von 3
Re: XML
Verfasst: Samstag 12. Mai 2012, 20:09
von Hyperion
MarcelF6 hat geschrieben:
Noch eine Schlussfrage: Was würdest du unter "Text des body-Elements" verstehen? Ist das einfach alles, inklusive Tags (wie z.B. titel) oder nur Text (ohne Tags) des nodes, den man übergibt?
Unter "Text" in Zusammenhang mit XML-Elementen würde ich nur an einen Text-Knoten denken. Dieser beinhaltet nur Text - folgende Tags repräsentieren ja dann neue, separate Knoten. (Bei `lxml` verzichtet man auf Text-Knoten und repräsentiert diese mittels der Properties `element.text` und `element.tail` - das ändert imho aber nichts am Prinzip.)
Allerdings mag es sein, dass sich die Bedeutung je nach Kontext ergibt; in einer dokumentzentrierten XML-Datei (ich denke bei "body" an XHTML...) könnte durchaus der gesamte Ast unterhalb des Elementes gemeint sein.
Steht obige Formulierung in einer Aufgabe? Oder wieso fragst Du das?
Re: XML
Verfasst: Samstag 12. Mai 2012, 20:22
von BlackJack
@MarcelF6: Falls hier wirklich von (X)HTML die Rede ist, dann kommt ``<title>`` nicht im ``<body>`` vor, sondern im ``<head>``. Die Beschreibung „Text des body-Elements” ist IMHO wie Hyperion schon schrieb in so einem Szenario nicht wirklich eindeutig. Das kann nur die Textknoten meinen, die direkte Kinder von ``<body>`` sind, aber auch rekursiv sämtliche Textknoten.
In letzteren Fall solltest Du noch mal die Methoden von `lxml.Element` anschauen, bevor Du etwas von Hand bastelst was es schon fertig gibt. Falls nur die direkten Kind-Textknoten gemeint sind, kommt man da mit XPath einfacher dran als sich alle `text`- und `tail`-Attribute in einer Schleife zusammen zu sammeln.
Re: XML
Verfasst: Samstag 12. Mai 2012, 21:17
von MarcelF6
Ok, dann liegt das nicht an meinem Verständnis - danke

Da man vorher den gesamten Text (inkl. Subelemente) zurückgeben musste, wird wohl das (also der ganze Text, jedoch ohne Tags) gemeint sein. Sonst würde man ja etwas retournieren, obwohl man es dann doch nicht braucht - was etwa so sinnvoll wäre wie tausend roots... (sorry nochmals

)
Re: XML
Verfasst: Samstag 12. Mai 2012, 21:44
von MarcelF6
Noch eine letzte Frage: Ich habe soeben festgestellt, dass ich in dem String, den ich retourniere (also in dem Text von 'node' inklusive der Subelemente) viele "None" habe. Wie bringt man die wieder weg; bzw. was müsste ich machen, damit sie gar nicht erst auftauchen?
Ich habs mal mit "replace" versucht, aber so werden natürlich nur jene "None" erwischt, die alleine stehen. "Leider" gibt es auch (vor allem) solche, die an andere Wörter kleben.
Ich hoffe, ihr habt eine gute Idee

Re: XML
Verfasst: Samstag 12. Mai 2012, 21:49
von Hyperion
Kann es sein, dass Du - wovon BlackJack Dir ja *abgeraten* hatte - einfach auf allen Children die `.text` und `.tail`-Properties wahllos aufrufst?
Zeig uns doch mal ein minimales, lauffähiges Beispiel (Mit dem nötigen XML als String vorliegend), mit welchem der Fehler nachvollziehbar wird!
Re: XML
Verfasst: Samstag 12. Mai 2012, 21:53
von BlackJack
@MarcelF6: `None` kann nicht „an andere Wörter (sic!) kleben”. `None` hat einen ganz anderen Datentyp als Wörter. Und nochmal der Hinweis auf die verschiedenen Methoden die `Element`-Objekte haben. Da ist eine dabei, die so ziemlich genau das macht was Du haben möchtest.
Re: XML
Verfasst: Sonntag 13. Mai 2012, 00:48
von MarcelF6
Hm doch, eigentlich verwende ich schon .text und .tail. Vor allem auch dewegen, weil ich die *passende* Methode leider noch nicht gefunden habe...
Code: Alles auswählen
def read(node):
s = " "
for element in doc.iter(node):
for ele in element.iter():
s += "".join(" %s %s" % (ele.text, ele.tail))
Die letzte Zeile könnte man natürlich auch einfacher als s += (" %s %s" % (ele.text, ele.tail)) schreiben - kommt ja hier aufs Gleiche raus.
Re: XML
Verfasst: Sonntag 13. Mai 2012, 06:48
von BlackJack
@MarcelF6: Wie hast Du denn gesucht? Zielgerichtetes Vorgehen wäre zum Beispiel sich den Typ von einem Element ausgeben zu lassen und dann in der API-Dokumentation zu dem konkreten Typ die Liste der Methoden durchgehen ob da Eine dabei ist mit der man über Textknoten iterieren kann. Oder man inspiziert so ein Objekt direkt in einer Python-Shell. Lässt sich mit `dir()` die Methoden auflisten und lässt sich von den Methoden die vom Namen her in Frage kommen mit `help()` den DocString anzeigen. Der Name der Methode ist offensichtlich.
Im `lxml`-Tutorial gibt es übrigens noch einen Weg das ganze noch einfacher zu erreichen: Ein Funktionsaufruf der nicht nur die Textknoten liefert, sondern die Texte sogar schon zu einer einzigen Zeichenkette zusammen setzt.
Den `join()`-Aufruf könntest Du in der Tat weg lassen. Wenn Du das weisst, warum schreibst Du solchen Unsinn dann überhaupt?
Das wiederholte ``+=`` sollte man auch vermeiden. Mit einem *sinvollen* `join()`-Aufruf auf einer Liste der Teilzeichenketten, die man vorher gesammelt hat.
In Deiner Funktion wird immer noch auf ein Objekt zugegriffen das nicht als Argument übergeben wurde. `doc` dürfte die Funktion so gar nicht kennen.
Re: XML
Verfasst: Sonntag 13. Mai 2012, 10:21
von MarcelF6
Danke für die Antworten!
Darf ich fragen, wo die einfachere Möglichkeit im Tutorial zu finden ist?
Re: XML
Verfasst: Sonntag 13. Mai 2012, 14:35
von BlackJack
@MarcelF6: Arbeite es doch einfach mal durch.
Re: XML
Verfasst: Sonntag 13. Mai 2012, 15:48
von MarcelF6
Ok, ich habs. Mit Hilfe von iterparse hats so geklappt, wie gewollt - und ohne "None" ' s

Besten Dank für die Hilfe und all die Tipps!

Re: XML
Verfasst: Sonntag 13. Mai 2012, 15:58
von Hyperion
Im Abschnitt
"Elements contains Text"hättest Du `etree.tostring(html, method="text")` gefunden
Darunter geht es dann auch gleich weiter mit der XPath-Methode...
und das hast Du nicht gefunden?
Re: XML
Verfasst: Sonntag 13. Mai 2012, 16:06
von BlackJack
Vor allem ist das alles deutlich *vor* den naheliegenden Lösungen im Tutorial. Und ich sehe auch nicht wie `iterparse()` eine Lösung sein kann, denn auch dort haben die Attribute `text` und `tail` den Wert `None` wenn kein Textknoten dafür vorhanden ist. Eben so wie das bei `Element`-Objekten zu erwarten ist. Was immer MarcelF6 da auch anders gemacht hat, es hat rein gar nichts mit `iterparse()` zu tun.
Re: XML
Verfasst: Sonntag 13. Mai 2012, 16:40
von MarcelF6
Stimmt. Mit iterparse() habe ich aber zusätzlich noch Abfragen gemacht, um zu prüfen, ob Text vorhanden ist oder nicht. Falls ja, so konnte der Inhalt dem String hinzugefügt werden, falls nein wurde einfach zum nächsten Element gegangen. Funktioniert super so, obwohl man es offenbar in ein paar wenigen Zeilen machen könnte.
ich habe den Artikel tatsächlich mal auf dem Bildschirm, ihn aber nur rasch überflogen.
Ich habe mir xpath mal angeschaut und wollte es für mein Beispiel anwenden. Allerdings erhalte ich so fast mehr Fehler, als ich es vorher mal hatte
Wie wäre es denn richtig?
Analog zum Beispiel im Tutorial habe ich es wie folgt probiert:
Code: Alles auswählen
import lxml.etree as ET
doc = ET.parse("beispiel.xml")
def lesen(node):
root = ET.Element("root") # neuer Wurzelknoten gemacht.
# nun müsste man body anfügen per: ET.SubElement(html, doc.xpath(node))
# ABER: dieses doc.xpath(node) kann man so natürlich nicht übergeben. .text würde auch nicht viel bringen
Naja, diese Variante wäre im Moment natürlich noch kleiner als meine jetzige. Aber ich denke, dass man - um den Text zu erhalten - durch alle Kindknoten durchiterieren müsste. In einem solchen Fall wäre die Variante nicht mehr viel einfacher...
Oder ginge es anders?
Re: XML
Verfasst: Sonntag 13. Mai 2012, 17:02
von BlackJack
@MarcelF6: Analog zu welchem Beispiel in welchem Tutorial ist der Quelltext‽ Und was soll das werden? Jedenfalls nicht die Textknoten in einem vorhandenen (Unter)Baum ermitteln, denn dann würde mich der Sinn vom erstellen eines neuen Elements nicht erschliessen‽
Es wurde zwar schon mal verlinkt, aber nur zur Sicherheit, das `lxml`-Tutorial ist das hier:
http://lxml.de/tutorial.html
Re: XML
Verfasst: Sonntag 13. Mai 2012, 18:00
von MarcelF6
Ich beziehe mich auf den Link von Hyperion (d.h. dasselbe Tutorial).
In dem Beispiel wird ja folgendes gemacht:
Code: Alles auswählen
print(html.xpath("string()")) # lxml.etree only!
TEXTTAIL
wobei vorher die Knoten und Texte erstellt werden:
Code: Alles auswählen
html = etree.Element("html")
body = etree.SubElement(html, "body")
body.text = "TEXT"
Nun habe ich ja aber schon ein existierendes xml-file mit existierenden Knoten und Texte. Der Clue ist ja der erste Code-Ausschnitt, denn dort wird der Text ausgegeben. Sehe ich das richtig, dass ich einfach per Iteration jeweils xpath aufrufen muss (und anstatt "string()" jeweils der Knoten bzw. Kindknoten angeben)?
Re: XML
Verfasst: Sonntag 13. Mai 2012, 18:35
von Hyperion
Probiere solche Kleinigkeiten doch einfach mal in einer Shell aus. Das geht doch viel schneller, als wenn Du hier auf eine Antwort wartest
Code: Alles auswählen
In [12]: data = """
<root>
Hallo<br/>Welt!. Du bist <b>so</b> schön!
</root>
"""
In [13]: from lxml import etree
In [14]: root = etree.fromstring(data)
In [15]: print(etree.tostring(root, method="text", encoding="utf-8"))
b'\nHalloWelt!. Du bist so sch\xc3\xb6n!\n'
oder auch mit XPath:
Code: Alles auswählen
In [16]: root.xpath("string()")
Out[16]: '\nHalloWelt!. Du bist so schön!\n'
Du musst da nix iterieren! Hättest Du doch leicht selber drauf kommen können, oder?

Re: XML
Verfasst: Sonntag 13. Mai 2012, 19:04
von MarcelF6
Danke für das Beispiel. Aber so ganz überzeugt hast du mich noch nicht

Darf ich dein Beispiel etwas erweitern?
Angenommen, wir haben:
Code: Alles auswählen
data = """
<root>
Hey
<titel>
Hallo<br/>Welt!. Du bist <b>so</b> schön!
</titel>
bye
</root>
"""
Und wir wollen nur das ausgeben, das in 'titel' und den Subelementen steht (d.h. "Hallo Welt! Du bist so schön!"). Dafür ist dann schon eine Iteration nötig, oder doch nicht?
Re: XML
Verfasst: Sonntag 13. Mai 2012, 19:14
von BlackJack
@MarcelF6: Nein. Dazu musst Du nur das Titelelement übergeben statt dem ganzen Baum. Und das *find*et man ohne Schleife.
Re: XML
Verfasst: Sonntag 13. Mai 2012, 19:26
von MarcelF6
Ok, mein Beispiel war dumm, bzw. noch nicht auf die Spitze getrieben.
Sagen wir, dass wir folgendes haben:
Code: Alles auswählen
data = """
<root>
Hey
<titel>
Hallo<br/>Welt!. Du bist <b>so</b> schön!
</titel>
<titel> Und ich hoffe <untertitel> das bleibt </untertitel> so! </titel>
bye
</root>
"""
Und wir immernoch am Text von allen 'titel' (inkl. Unterelemente) interessiert sind. In dem Fall kommt man tatsächlich nicht um die Iteration herum, oder?