XML mit LXML einlesen, wobei die Struktur nicht alltäglich ist.
Hallo Python-De Mailingliste, ich habe für euch eine knifflige Aufgabe. Diese habe ich zwar gelöst habe, jedoch mit einem hässlichem workaround. Im speziellem muss ich mehrere XML Files auslesen, die in Summe ca. 140GB Blog Daten enthalten. Die Einträge im XML File beginnen alle mit: <item> </item> <item> .... dies bedeutet, dass die Files KEIN Root Element enthalten. Hier hatte ich schon mein erstes Problem. Denn wie kann man mit LXML ein File parsen, sei es mit dem parser als auch mit dem Iterparser, wenn es kein Root Element beinhaltet. Ich habe immer nur das erste Item erhalten, und die restlichen waren für mich unsichtbar. Dies habe ich nun gelöst, indem ich zu Beginn <root> und am Ende des Files </root> hinzugefügt habe. Wie bereits gesagt, eine nicht sehr schöne Notlösung. Habt ihr eine Idee wie das auch anders gehen könnte? Das zweite Problem das ich hatte, war dass es Element gibt die wie folgt ausschauen: <source:title></source:title> <weblog:weblog_type></weblog:weblog_type> LXML interpretierte hier immer source als auch weblog als Namespaces, die im Header jedoch nicht definiert wurden. Ich kann diese Einträge nun auch nicht ändern, da ich diese Daten so erhalten habe, und es keine weiteren Zusatzinformationen dazu gibt. Ich hab hier nun beim XMLParser den tag recover=True verwendet. Auch hier möchte ich euch fragen, ob dies die Einzige Lösung ist? Oder ihr vielleicht einen besseren Lösungsansatz vorschlagen könntet? Der Sourcecode sieht wie folgt aus: parser = etree.XMLParser(recover=True) infile = 'part-1.xml' results = etree.parse(infile, parser) count_en_blogs = 0 sum = 0 for e in results.getiterator('item'): if 'en' == e.find('lang').text: #print e.find('lang').text count_en_blogs += 1 sum +=1 else: sum +=1 print ("Die Resource beinhaltet %s Englische Blogeintraege und gesamt %s Eintraege" ) %(count_en_blogs, sum) Vielen dank im voraus für jegliche Anwort. lg, Tom
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 24.03.2009 11:58 Uhr, Thomas Gebhard wrote:
Hallo Python-De Mailingliste,
ich habe für euch eine knifflige Aufgabe. Diese habe ich zwar gelöst habe, jedoch mit einem hässlichem workaround. Im speziellem muss ich mehrere XML Files auslesen, die in Summe ca. 140GB Blog Daten enthalten.
140GB??
Die Einträge im XML File beginnen alle mit:
<item> </item> <item> ....
dies bedeutet, dass die Files KEIN Root Element enthalten.
Kein Root Element -> Du hast *kein* XML.
Hier hatte ich schon mein erstes Problem. Denn wie kann man mit LXML ein File parsen, sei es mit dem parser als auch mit dem Iterparser, wenn es kein Root Element beinhaltet. Ich habe immer nur das erste Item erhalten, und die restlichen waren für mich unsichtbar.
Dies habe ich nun gelöst, indem ich zu Beginn <root> und am Ende des Files </root> hinzugefügt habe. Wie bereits gesagt, eine nicht sehr schöne Notlösung. Habt ihr eine Idee wie das auch anders gehen könnte?
Jeder vernünftige XML Parser setzt gültiges XML voraus. Also gibt es zwei Möglichkeiten: Du machst aus Deinem Datengrab gültiges XML oder Du schreibst Dir einen entsprechenden Parser selbst. - -aj - -- ZOPYX Ltd. & Co. KG - Charlottenstr. 37/1 - 72070 Tübingen - Germany Web: www.zopyx.com - Email: info@zopyx.com - Phone +49 - 7071 - 793376 Registergericht: Amtsgericht Stuttgart, Handelsregister A 381535 Geschäftsführer/Gesellschafter: ZOPYX Limited, Birmingham, UK - ------------------------------------------------------------------------ E-Publishing, Python, Zope & Plone development, Consulting -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (Darwin) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAknIyS4ACgkQCJIWIbr9KYwzjgCeMQqtHTa7cUxFjvEwvzaXnDm5 NSIAn3yvAe6woZtwnR2mMVLXPCdLHr/E =kodV -----END PGP SIGNATURE-----
Andreas Jung wrote:
140GB??
jeep, http://www.icwsm.org/2009/data/index.shtml -> ICWSM 2009 Spinn3r Blog Dataset
Die Einträge im XML File beginnen alle mit:
<item> </item> <item> ....
dies bedeutet, dass die Files KEIN Root Element enthalten.
Kein Root Element -> Du hast *kein* XML.
Jeder vernünftige XML Parser setzt gültiges XML voraus. Also gibt es zwei Möglichkeiten: Du machst aus Deinem Datengrab gültiges XML oder Du schreibst Dir einen entsprechenden Parser selbst.
Ok, da werde ich dann meine aktuelle Lösung beibehalten. lg, tom
Thomas Gebhard schrieb:
ich habe für euch eine knifflige Aufgabe. Diese habe ich zwar gelöst habe, jedoch mit einem hässlichem workaround. Im speziellem muss ich mehrere XML Files auslesen, die in Summe ca. 140GB Blog Daten enthalten.
Jemineh. Ich hoffe doch, die einzelnen Dateien sind kleiner?
Die Einträge im XML File beginnen alle mit:
<item> </item> <item> ....
dies bedeutet, dass die Files KEIN Root Element enthalten.
Wie mein Vorredner schon sagte: kein XML.
Dies habe ich nun gelöst, indem ich zu Beginn <root> und am Ende des Files </root> hinzugefügt habe.
Wenn du die Feed-Parser API benutzt, musst du zumindest die Dateien dazu nicht anfassen. http://codespeak.net/lxml/parsing.html#the-feed-parser-interface Eine andere Möglichkeit ist es, selbst ein file-ähnliches Objekt um das eigentliche Dateiobjekt herum zu schreiben, das bei .read() zuerst das öffnende Root-Tag, dann den Dateiinhalt (häppchenweise) und danach das schließende Root-Tag zurückliefert. Das funktioniert dann auch mit iterparse(), so dass du auch noch alle Dateien auf einen Schlag hintereinander weg einlesen könntest, indem du sie einfach in deinem file-like hintereinander einliest. Also sowas (völlig ungetestetes): class bigfile(object): def __init__(self, files): self.files = iter(files) self.next_file = None def read(self, size=8192): if self.files is None: return '' data = '' try: if self.next_file is None: data = "<root>" self.next_file = self.files.next() else: while not data: self.next_file = self.files.next() data = self.next_file.read(size) except StopIteration: data += "</root>" self.files = None return data
Das zweite Problem das ich hatte, war dass es Element gibt die wie folgt ausschauen: <source:title></source:title> <weblog:weblog_type></weblog:weblog_type>
LXML interpretierte hier immer source als auch weblog als Namespaces, die im Header jedoch nicht definiert wurden.
Also noch weniger XML, na super. Dann benutz doch als öffnendes Root-Tag '<root xmlns:source="" xmlns:weblog="">' Damit setzt du die Präfixe auf den leeren Namespace, so dass die Elemente dann ohne Namespace aus dem Parser zurück kommen. Hilft dir das? Stefan
Stefan Behnel wrote:
Thomas Gebhard schrieb:
ich habe für euch eine knifflige Aufgabe. Diese habe ich zwar gelöst habe, jedoch mit einem hässlichem workaround. Im speziellem muss ich mehrere XML Files auslesen, die in Summe ca. 140GB Blog Daten enthalten.
Jemineh. Ich hoffe doch, die einzelnen Dateien sind kleiner?
Jeep, die einzelnen Dateien bewegen sich im Rahmen von 5MB ~ 120MB
Wenn du die Feed-Parser API benutzt, musst du zumindest die Dateien dazu nicht anfassen.
http://codespeak.net/lxml/parsing.html#the-feed-parser-interface
Danke für den Hinweis, das muss ich mir heute Abend genauer anschauen.
Eine andere Möglichkeit ist es, selbst ein file-ähnliches Objekt um das eigentliche Dateiobjekt herum zu schreiben, das bei .read() zuerst das öffnende Root-Tag, dann den Dateiinhalt (häppchenweise) und danach das schließende Root-Tag zurückliefert.
Das funktioniert dann auch mit iterparse(), so dass du auch noch alle Dateien auf einen Schlag hintereinander weg einlesen könntest, indem du sie einfach in deinem file-like hintereinander einliest.
Also sowas (völlig ungetestetes):
class bigfile(object): def __init__(self, files): self.files = iter(files) self.next_file = None def read(self, size=8192): if self.files is None: return '' data = '' try: if self.next_file is None: data = "<root>" self.next_file = self.files.next() else: while not data: self.next_file = self.files.next() data = self.next_file.read(size) except StopIteration: data += "</root>" self.files = None return data
Danke für den Hinweis, ich werde auch das heute Abend ausprobieren.
Das zweite Problem das ich hatte, war dass es Element gibt die wie folgt ausschauen: <source:title></source:title> <weblog:weblog_type></weblog:weblog_type>
LXML interpretierte hier immer source als auch weblog als Namespaces, die im Header jedoch nicht definiert wurden.
Also noch weniger XML, na super.
Dann benutz doch als öffnendes Root-Tag
'<root xmlns:source="" xmlns:weblog="">'
Damit setzt du die Präfixe auf den leeren Namespace, so dass die Elemente dann ohne Namespace aus dem Parser zurück kommen.
Das habe ich jetzt schon ausprobiert, jedoch bekomme ich die Meldung dass die Angegebene URI nicht gültig ist: <root xmlns:source="" xmlns:weblog="" xmlns:dc="" xmlns:atom=""> lxml.etree.XMLSyntaxError: xmlns:source: '' is not a valid URI, line 1, column 19 Danke und lg, Tom
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 24.03.2009 14:56 Uhr, Thomas Gebhard wrote:
Das habe ich jetzt schon ausprobiert, jedoch bekomme ich die Meldung dass die Angegebene URI nicht gültig ist: <root xmlns:source="" xmlns:weblog="" xmlns:dc="" xmlns:atom=""> lxml.etree.XMLSyntaxError: xmlns:source: '' is not a valid URI, line 1, column 19
Die Fehlermeldung ist selbstsprechend. Alle Namespace Deklarationen via xmlns:XXXX benötigen eine URL als Wert. Näheres erklärt Dir jedes XML Tutorial. Andreas -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (Darwin) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAknI7u8ACgkQCJIWIbr9KYykqgCgtuVUc7150BWeAr+QAHn5D87l DSYAniZ8eeufmK95s/e7RSM0QU7aDDO6 =6c2i -----END PGP SIGNATURE-----
Thomas Gebhard schrieb:
Stefan Behnel wrote:
Also sowas (völlig ungetestetes):
class bigfile(object): def __init__(self, files): self.files = iter(files) self.next_file = None def read(self, size=8192): if self.files is None: return '' data = '' try: if self.next_file is None: data = "<root>" self.next_file = self.files.next() else: # ähem ... data = self.next_file.read(size) while not data: self.next_file = self.files.next() data = self.next_file.read(size) except StopIteration: data += "</root>" self.files = None return data
Das zweite Problem das ich hatte, war dass es Element gibt die wie folgt ausschauen: <source:title></source:title> <weblog:weblog_type></weblog:weblog_type>
LXML interpretierte hier immer source als auch weblog als Namespaces, die im Header jedoch nicht definiert wurden.
Dann benutz doch als öffnendes Root-Tag
'<root xmlns:source="" xmlns:weblog="">'
Damit setzt du die Präfixe auf den leeren Namespace, so dass die Elemente dann ohne Namespace aus dem Parser zurück kommen.
Das habe ich jetzt schon ausprobiert, jedoch bekomme ich die Meldung dass die Angegebene URI nicht gültig ist: <root xmlns:source="" xmlns:weblog="" xmlns:dc="" xmlns:atom=""> lxml.etree.XMLSyntaxError: xmlns:source: '' is not a valid URI, line 1, column 19
Richtig, libxml2 2.7.x, ich vergaß. Dann setz eben die richtigen Namespace URIs ein (oder eben nur irgendwelche). Damit sehen dann zwar die Tags der entsprechenden Elemente etwas länger aus, aber das sind auch nur Strings, die sich wunderbar in eine Variable schieben lassen. Und dafür hast du dann am Ende Namespace-korrektes XML. Stefan
participants (3)
-
Andreas Jung
-
Stefan Behnel
-
Thomas Gebhard