pyxml: verarbeiten eines xml

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
paddy2706
User
Beiträge: 18
Registriert: Donnerstag 10. April 2008, 22:20

hallo,

ich muss mehrere xml-dateien verarbeiten und würde mir gerne dazu ein kleines python-skript schreiben.

Code: Alles auswählen

import xml.dom.minidom

# test
xmlbible=xml.dom.minidom.parse("/home/paddy/Desktop/Dateien/NEÜr12/sf_neue_rev12.xml")

notetags = xmlbible.getElementsByTagName("NOTE")

for notetag in notetags.item
    # das element mit seinem inhalt löschen
wie kann ich diese elemente löschen?

ich hoffe der code ist klar.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Nutzt du wirklich das exterme Modul PyXML oder könntest du da ElementTree oder lxml verwenden?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
paddy2706
User
Beiträge: 18
Registriert: Donnerstag 10. April 2008, 22:20

ich nehm auch ne andere bibliothek wenn das mit pyxml net geht (oder nicht so einfach)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mit ElementTree geht das per ``remove``:

Code: Alles auswählen

>>> from xml.etree import ElementTree as ET
>>> root = ET.fromstring('<root><child /></root>')
>>> root.findall('child')
[<Element child at 7f84257a4bd8>]
>>> root.remove(root.findall('child')[0])
Wird schon auch irgendwie mit PyXML gehen, aber warum DOM nutzen, wenn es nicht unbedingt sein muss.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
paddy2706
User
Beiträge: 18
Registriert: Donnerstag 10. April 2008, 22:20

hey leonidas,

danke für die antwort. mit elementtree funktioniert das ja wirklich prima. leider habe ich ein kleines problem. ich muss nun aus folgendem

Code: Alles auswählen

<parent>Dies ist ein Text mit <children>Hinweis: Text ist doof</children> mit kind</parent>
das machen:

Code: Alles auswählen

<parent>Dies ist ein Text mit mit kind</parent>
wenn ich jetzt aber

Code: Alles auswählen

parent = ET.ElementTree(None, file)
for each child in parent.getchildren():
    parent.remove(child)
mache, kommt als ergebniss

Code: Alles auswählen

<parent>Dies ist ein Text mit </parent>
heraus.
Das ist leider etwas unpraktisch. Hat noch jemand einen Hinweis, wie das zu lösen ist, oder ist das gar ein Bug?
Danke,
paddy2706
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

Hi Paddy meiner Meinung nach ist das ein sehr komischer XML-Aufbau den du da zeigst.

So wie Elementtree arbeitet gehört der hintere Text "mit Kind"
zu dem tail des Knoten <child> und wird somit zurecht mit gelöscht.

Ist das ein vorgegebenes Format das du da hast, oder ein eigenes?
Wenn es dein eigenes ist, würde ich es überdenken.
Ansonsten musst du dir die Informationen von child.tail kopieren bevor du den Knoten löschst.

Code: Alles auswählen

In [15]: import xml.etree.ElementTree as ET

In [16]: xml = "<parent>Dies ist ein Text mit <children>Hinweis: Text ist doof</children> mit kind</parent>"

In [17]: r = ET.XML(xml)

In [18]: c = list(r)[0]

In [19]: c
Out[19]: <Element children at eb9d50>

In [20]: c.tail
Out[20]: ' mit kind'
paddy2706
User
Beiträge: 18
Registriert: Donnerstag 10. April 2008, 22:20

Hey Zap,

leider ist das ein vorgegebenes Format, das ich zu verarbeiten habe.
Danke für deinen Tip. Mit .tail funktioniert das prima.

:)
BlackJack

@Zap: XHTML oder DocBook wären dann also "komisch"!? Ich finde das gezeigte XML jedenfalls völlig normal.
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

BlackJack hat geschrieben:@Zap: XHTML oder DocBook wären dann also "komisch"!? Ich finde das gezeigte XML jedenfalls völlig normal.
:oops: Ok, DocBook kannte ich noch nicht, aber logisch bei XHTML leuchtet das völlig ein...
Nehme meine Aussage von heute morgen somit zu 100% zurück! ;)
paddy2706
User
Beiträge: 18
Registriert: Donnerstag 10. April 2008, 22:20

so. mit eurer hilfe bin ich schonmal sehr weit gekommen. jetzt stehe ich vor einem anderen problem:

mit

Code: Alles auswählen

for verse_child in verse.getchildren():
	if type(verse_child.tail) != type(None):
		verse.text = verse.text + verse_child.tail
	verse.remove(verse_child)
habe ich die subs gelöscht und ggf. die tails dem text angehängt.

wenn jetzt in meinem xml file ein element habe, das wie dieses aussieht:

Code: Alles auswählen

<parent>Dies ist ein Text <children>Hinweis: Text ist doof</children> mit <children>einem zweiten</children> kind</parent>
wird mit der funktion oben daraus

Code: Alles auswählen

<parent>Dies ist ein Text  kind</parent>
das ist ja zum mäusemelken!

hoffe jemand von euch hat noch eine idee, warum das so passiert.

EDIT:
komischerweise funktioniert es wenn ich die ganze sachen händisch in der konsole durchlaufen lasse:

Code: Alles auswählen

>>> a = ET.fromstring("<parent>Dies ist ein Text <children>Hinweis: Text ist doof</children> mit <children>einem zweiten</children> kind</parent>")
>>> ET.dump(a)
<parent>Dies ist ein Text <children>Hinweis: Text ist doof</children> mit <children>einem zweiten</children> kind</parent>
>>> a.getchildren()
[<Element children at 12bc998>, <Element children at 12bcc68>]
>>> a.getchildren()[0].tail
' mit '
>>> a.text = a.text + a.getchildren()[0].tail
>>> a.text
'Dies ist ein Text  mit '
>>> a.remove(a.getchildren()[0])
>>> ET.dump(a)
<parent>Dies ist ein Text  mit <children>einem zweiten</children> kind</parent>
>>> a.text = a.text + a.getchildren()[0].tail
>>> a.text
'Dies ist ein Text  mit  kind'
>>> a.remove(a.getchildren()[0])
>>> ET.dump(a)
<parent>Dies ist ein Text  mit  kind</parent>
>>> 
BlackJack

Kann es sein das über dem Satz mit dem Mäusemelken nicht die Problemausgabe steht, sondern das was Du haben möchtest? Ansonsten verstehe ich das Problem nicht!?

Setz mal um den Aufruf von ``verse.getchildren()`` ein `list()`, sonst löscht Du nämlich mit `remove()` Elemente aus einer Liste über die Du iterierst, das geht in den meisten Fällen schief.

Die Abfrage auf `None` ist umständlich: ``if verse_child.tail is not None:`` ist etwas direkter und einfacher.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

BlackJack hat geschrieben:@Zap: XHTML oder DocBook wären dann also "komisch"!? Ich finde das gezeigte XML jedenfalls völlig normal.
Ich finde es eher andersrum, das ``tail`` ist beim ElementTree etwas seltsam. Zwar war ich kein allzu großer Freund der Textknoten in DOM, aber die haben besser zum Principle of least Surprise gepasst.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
lunar

@BlackJack
In seinem Beispiel fehlt am Schluss der Text zwischen den beiden Kindelementen. Außerdem erzeugt ".getchildren()" meines Wissens eine separate Liste, so dass man problemlos per ".remove()" aus dem Element entfernen kann.

@paddy2706

Code: Alles auswählen

from lxml import etree
parent = etree.fromstring('<parent>Dies ist ein Text <children>Hinweis: Text ist doof</children> mit <children>einem zweiten</children> kind</parent>')
text = ''.join(parent.xpath('./text()'))
parent.clear()
parent.text = text
Dazu benötigst du allerdings das Modul "lxml", welches du im Cheeseshop findest.

Ansonsten tut es folgendes auch mit der mitgelieferten etree-Implementierung:

Code: Alles auswählen

from xml.etree import ElementTree as etree
parent = etree.fromstring('<parent>Dies ist ein Text <children>Hinweis: Text ist doof</children> mit <children>einem zweiten</children> kind</parent>')
text = [parent.text]
for element in parent.findall('children'):
    text.append(element.tail)
    parent.remove(element)
parent.text = ''.join(text)
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

lunar hat geschrieben:Kindelementen. Außerdem erzeugt ".getchildren()" meines Wissens eine separate Liste, so dass man problemlos per ".remove()" aus dem Element entfernen kann.
Nein das macht ElementTree (zumindest das build-in von Python 2.5) nicht.

Code: Alles auswählen

In [83]: id(r._children)
Out[83]: 15341248

In [84]: id(r.getchildren())
Out[84]: 15341248
Der Vorschlag von Blackjack war somit schon richtig:

Code: Alles auswählen

In [91]: root = ET.fromstring('<parent>Dies ist ein Text <children>Hinweis: Text ist doof</children> mit <children>einem
 zweiten</children> kind</parent>')
In [92]: for child in list(root):
   ....:     root.text += child.tail
   ....:     root.remove(child)
   ....:
   ....:

In [93]: root.text
Out[93]: 'Dies ist ein Text  mit  kind'
BlackJack

@lunar: Aber er schreibt danach das es beim manuellen Versuch im Interpreter klappt und da kommt am Ende *das gleiche* heraus wie bei dem (angeblichen) Beispiel wo es nicht klappt. Soweit ich das verstanden hatte sollen die Tags inklusive Inhalt entfernt werden.
lunar

Zap hat geschrieben:
lunar hat geschrieben:Kindelementen. Außerdem erzeugt ".getchildren()" meines Wissens eine separate Liste, so dass man problemlos per ".remove()" aus dem Element entfernen kann.
Nein das macht ElementTree (zumindest das build-in von Python 2.5) nicht.

Code: Alles auswählen

In [83]: id(r._children)
Out[83]: 15341248

In [84]: id(r.getchildren())
Out[84]: 15341248
Gut, da habe ich etwas voreilig von lxml.etree auf die Standardbibliothek geschlossen. lxml.etree erzeugt nämlich eine separate Liste, deren Modifikation keine Auswirkungen auf den Tree hat:

Code: Alles auswählen

>>> parent.getchildren()
[<Element children at 1635838>, <Element children at 1635890>]
>>> parent.getchildren().pop()
<Element children at 1635890>
>>> parent.getchildren()
[<Element children at 1635838>, <Element children at 1635890>]
@BlackJack
Da kommt nicht das gleiche raus: Bei seinem Code ist das Ergebnis "<parent>Dies ist ein Text kind</parent>", in der interaktiven Sitzung allerdings "<parent>Dies ist ein Text mit kind</parent>".
Antworten