lxml erfindet scheinbar zeichen Oo

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
patmaster
User
Beiträge: 106
Registriert: Donnerstag 3. Februar 2011, 17:21

Hi,

Ich habe folgendes Script:

Code: Alles auswählen

# -*- coding: utf-8 -*-
'''
Created on 11.04.2012

@author: SzaboP
'''
import os
import MySQLdb
import traceback
from lxml import etree

def connect():
    mysql_opts = { 
    'host': "xxx", 
    'user': "xxx", 
    'pass': "xxx", 
    'db':   "xxx" 
    } 
    mysql = MySQLdb.connect(mysql_opts['host'], mysql_opts['user'], mysql_opts['pass'], mysql_opts['db'])
    return mysql

def mainArtikel(folder, targetpath):
    try:
        #Variablen die wir brauchen
        dbconnection = connect()
        cursor = dbconnection.cursor()
        parser = etree.XMLParser(recover=True, resolve_entities=False)
        
        for dirpath, dirname, filenames in os.walk(folder):
            try: 
                for file in filenames:
                    #print(file)
                    filepath = os.path.join(dirpath, file)
                    filetargetpath = os.path.join(targetpath + "%s" %file)
                    #Hole entsprechende Autoren aus der DB
                    cursor.execute("Select output FROM documents WHERE filename='%s'" % file)
                    authorstring = cursor.fetchone()
                    if not(authorstring):
                        continue
                    #print("Da hol ich doch was...")
                    fileasxml = etree.parse(filepath, parser)
                    print(etree.tostring(fileasxml))
                    #Wir muessen hier ersetzen und was zum Trennen einbauen, sonst kommt etree damit nicht klar.
                    authorstring = authorstring[0].replace(">", ">;")
                    elements = []
                    for authorstr in authorstring.split(";"):
                        try:
                            dbauthorelement = etree.XML(authorstr.strip())
                            elements.append(dbauthorelement)
                        except:
                            continue
                    #Loesche evtl. vorhandene meta_autor - Tags
                    for elems in fileasxml.xpath("//meta_autor"):
                            elems.getparent().remove(elems)                            
                    for elem in elements:
                        authornew = etree.Element("meta_autor")
                        vorname = etree.SubElement(authornew, "vorname")
                        vorname.text = elem.get("fn")                     
                        nachname = etree.SubElement(authornew, "nachname")
                        nachname.text = elem.get("ln")
                        authornew.set("rolle", "autor")                    
                        fileasxml.xpath("//meta")[0].append(authornew)
                    fileasxml.write(filetargetpath, xml_declaration=True, encoding="UTF-8")
            except:
                print(traceback.format_exc())
    except:
        print(traceback.format_exc())
Das ganze macht was es soll für 90% der files. Bei manchen jedoch sind im output plötzlich Zeichen (Ö) die im input an der Stelle nicht da sind.
Es scheint so als parst er das file schon so ein, also print(etree.tostring(fileasxml)) enthält dieses Zeichen auch schon.

Auszug aus xml:

input:

Code: Alles auswählen

<bibliographie>
        <zeitschrift alternativ="&Ouml;Bl-LS">&Ouml;Bl</zeitschrift>
        <jahrgang>2002</jahrgang>
        <ausgabe datumstyp="offiziell" erscheinungsdatum="01.03.2002" art="Heft">2</ausgabe>
        <seite-von>63</seite-von>
        <seite-bis>63</seite-bis>
        <artikel-nr>32</artikel-nr>
        <autor>Helmut Gamerith</autor>
        <verknuepfung-mit-urteil akz-senat="4" akz-lfd-nummer="261" datum="13.11.2001" akz-jahr="01"
            gericht="OGH" akz-registerzeichen="Ob"/>
    </bibliographie>
output:

Code: Alles auswählen

<bibliographie>
        &Ouml;<zeitschrift alternativ="Bl-LS">&Ouml;Bl</zeitschrift>
        <jahrgang>2002</jahrgang>
        <ausgabe datumstyp="offiziell" erscheinungsdatum="01.03.2002" art="Heft">2</ausgabe>
        <seite-von>63</seite-von>
        <seite-bis>63</seite-bis>
        <artikel-nr>32</artikel-nr>
        <autor>Helmut Gamerith</autor>
        <verknuepfung-mit-urteil akz-senat="4" akz-lfd-nummer="261" datum="13.11.2001" akz-jahr="01" gericht="OGH" akz-registerzeichen="Ob"/>
    </bibliographie>
Jemand ne Idee ?

Ist vermutlich ein encoding Problem, aber da sollten doch auch keine Zeichen auftauchen Oo

Danke im Vorraus!
Zuletzt geändert von Hyperion am Freitag 4. Mai 2012, 11:20, insgesamt 1-mal geändert.
Grund: Code in Python-Code Tags gesetzt. Letzten Absatz aus Code heraus geholt.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

"&Ouml;" ist ein HTML-Entity und hat in einer XML-Datei gar nichts zu suchen. XML würdest du mit lxml.etree.XML() parsen. Du scheinst den HTML-Modus von lxml zu benutzen. Ich tippe daher darauf, dass deine Eingabedaten korrupt sind. Vielleicht korrigierst du sie, indem du manuell "&Ouml;" durch "&#214;" ersetzt.

Stefan
patmaster
User
Beiträge: 106
Registriert: Donnerstag 3. Februar 2011, 17:21

sma hat geschrieben:"&Ouml;" ist ein HTML-Entity und hat in einer XML-Datei gar nichts zu suchen. XML würdest du mit lxml.etree.XML() parsen. Du scheinst den HTML-Modus von lxml zu benutzen. Ich tippe daher darauf, dass deine Eingabedaten korrupt sind. Vielleicht korrigierst du sie, indem du manuell "&Ouml;" durch "&#214;" ersetzt.

Stefan
Danke für deine Antwort.
Ich glaube ehrlich gesagt nicht das ich den HTML-Modus aktiviert habe...wo soll das denn passieren ?
Wenn ich nur ersetze bringt, es mir leider nix weil an der Stelle eigentlich nix sein sollte.

Mir ist aufgefallen das das Zeichen aus dem Attribut, vor das Element wandert...zusehen in meinen XML-Auszügen.
Kann sich das jemand erklären ?
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Du hast am Konstruktor des Parsers das recover-Flag gesetzt. Nimm das doch mal weg und schau Dir die Fehlermeldung an. Mit dem Flag versucht er halt, irgendwie kaputtes XML zu reparieren. Das Verhalten ist dann nicht mehr durch die Spec abgedeckt. Da versucht dann jeder Parser, irgendwie damit klar zu kommen. Problem ist, dass das &Ouml; in dem Attribut wahrscheinlich nicht erlaubt ist (Fehlermeldung ohne recover-Flag sollte Aufschluss geben). Gibts denn eine DTD zu Deinem Format?
patmaster
User
Beiträge: 106
Registriert: Donnerstag 3. Februar 2011, 17:21

jerch hat geschrieben:Du hast am Konstruktor des Parsers das recover-Flag gesetzt. Nimm das doch mal weg und schau Dir die Fehlermeldung an. Mit dem Flag versucht er halt, irgendwie kaputtes XML zu reparieren. Das Verhalten ist dann nicht mehr durch die Spec abgedeckt. Da versucht dann jeder Parser, irgendwie damit klar zu kommen. Problem ist, dass das &Ouml; in dem Attribut wahrscheinlich nicht erlaubt ist (Fehlermeldung ohne recover-Flag sollte Aufschluss geben). Gibts denn eine DTD zu Deinem Format?
Danke für deine Antwort.

Es kommt kein Fehler wenn ich recover wegnehme. Ja es gibt eine DTD und der Input ist Valide.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

patmaster hat geschrieben: Das ganze macht was es soll für 90% der files. Bei manchen jedoch sind im output plötzlich Zeichen (&Ouml;) die im input an der Stelle nicht da sind.
Also in Deinen XML-Auszügen steht das Zeichen da aber sehr wohl schon im Attribut "alternativ" vom "zeitschrift"-Tag! Das deutet doch einfach auf einen korrupten Datensatz hin?

Generell kann man zu Deinem Quellcode einiges sagen, was nicht spezifisch mit dem Fehler zusammen hängt:

- Du fängst *alle* Exceptions ab, nicht eine spezielle. Das solltest Du nie tun. Im übrigen ist das eigentlich auch keine sinnvolle Behandlung, da Du den Fehler ja nur stumpf ausgibst. In einem anderen Fall, "verschluckst" Du *alle* Fehler. Das ist nicht gut! Fange nur spezielle Typen von Ausnahmen ab, deren Auftreten Du eingeplant hast.

- Queries sollte man nie mittels Stringformatierung zusammenbauen. Nutze dafür die Möglichkeiten des verwendeten DB-Moduls.

- Das hier:

Code: Alles auswählen

filetargetpath = os.path.join(targetpath + "%s" %file)
ist unnötig und kompliziert. Das `join` ist hier überflüssig, da Du diesem *einen* String übergibst. Den kann und braucht es nicht mehr zusammenbauen ;-) Und ein `"%s" % name` ist eine komplizierte Darstellung von `name`. Zumindest, wenn keine Typkonvertierung stattfindet; aber da sollte man dann eher mit `str` arbeiten.
Du willst:

Code: Alles auswählen

os.path.join(targetpath, file)
- `file` als Name ist schlecht gewählt. Zum einen verbirgt sich dahinter ja kein `file`-Objekt, sondern der *Dateiname*, zum anderen überschreibst Du damit in Built-in. Wieso nicht `filename`?

- Beachte PEP8. Funktionen werden nicht in "mixedCase" benannt, sondern als "lowercase_with_underscores".

- Da Du offensichtlich Python3 verwendest, ist das Encoding-Cookie überflüssig.

Wieso definierst Du den XML-Parser so:

Code: Alles auswählen

parser = etree.XMLParser(recover=True, resolve_entities=False)
Was genau bewirkt das?

Zudem finde ich diese Passage erklärungsbedürftig:

Code: Alles auswählen

#Wir muessen hier ersetzen und was zum Trennen einbauen, sonst kommt etree damit nicht klar.
Was genau willst Du da machen?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
patmaster
User
Beiträge: 106
Registriert: Donnerstag 3. Februar 2011, 17:21

Danke für deine ausführliche Antwort.
Das mein Skript nicht wirklich "eloquent" ist war mir schon klar und ich stimmte eigentlich überall mit dir überein. Wenn die Zeit fehlt wirft man schnell mal alle Guidelines über Board, vor allem wenn man das Ding nur 1 Mal braucht und es dann eigentlich wegwerfen könnte.

Ich definiere dem Parser selbst, damit ich ihm die Parameter (recover, resolve_entities) übergeben kann. Falls ich da die Defaultwerte übergebe, dann ist es natürlich unnötig ^^

Das Kommentar ist natürlich nicht gerade sprechend. In dem der DB stehen mehrere Elemente ohne Trennzeichen. Daraus konnte ich keine Elemente erzeugen, wegen ich zuerst ein Trennzeichen einbaue und dann einzelne Elemente erstelle.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

patmaster hat geschrieben:Wenn die Zeit fehlt wirft man schnell mal alle Guidelines über Board, vor allem wenn man das Ding nur 1 Mal braucht und es dann eigentlich wegwerfen könnte.
Tja, leider schleichen sich durch solche "Schlampereien" eher auch mal konzeptionelle Fehler ein ;-) Außerdem ist es für uns "Helfer" leichter, PEP8 konformen Code zu lesen.
patmaster hat geschrieben: Ich definiere dem Parser selbst, damit ich ihm die Parameter (recover, resolve_entities) übergeben kann. Falls ich da die Defaultwerte übergebe, dann ist es natürlich unnötig ^^
Ich hatte ja auf eine Erklärung gehofft, damit ich nicht selber in der Doku nachgucken muss ;-) Nun gut, habe ich das eben mal getan :-D
Das Kommentar ist natürlich nicht gerade sprechend. In dem der DB stehen mehrere Elemente ohne Trennzeichen. Daraus konnte ich keine Elemente erzeugen, wegen ich zuerst ein Trennzeichen einbaue und dann einzelne Elemente erstelle.
Ok, also hat das ganze nichts mit dem Problem an sich zu tun und ist die eigentliche Arbeit, die Du mit dem Script verrichten willst. Mir ist zwar nicht klar, was und wieso Du das machst, aber sei 's drum.

@Problem: Stehen diese Zeichen denn wirklich so in der DB? Wenn ja, kannst Du die nicht einfach löschen? Einen echten Sinn haben die ja anscheinend nicht...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
patmaster
User
Beiträge: 106
Registriert: Donnerstag 3. Februar 2011, 17:21

Hyperion hat geschrieben: @Problem: Stehen diese Zeichen denn wirklich so in der DB? Wenn ja, kannst Du die nicht einfach löschen? Einen echten Sinn haben die ja anscheinend nicht...
Nein dieses Zeichen steht so nicht in der DB. Den Teil des XMLs wo das Zeichen herkommt rühr ich gar nicht an.
Da wandert einfach das &Ouml; aus dem Attribut, vor das Element....crazy :)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Dann sag uns doch aber mal, *wann* (bzw *wo* im Code) dieses Zeichen auftritt. Oben hast Du uns einen Datensatz gezeigt, in dem das Zeichen enthalten war - das ist also kein DB-Auszug, sondern ein String, der später im Code entstanden ist?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Also Dein Problem ist, dass Du versucht, Dokumente mit HTML eigenen Entities mit XML (dort per se unbekannt) zu verarzten. Ich verweise Dich mal hierauf: http://jars.de/sonstiges/xml-und-html-entities
Da findest Du mögliche Wege, das Problem anzugehen. Mit recover und Nichtauflösen der Enititäten löst Du nämlich nicht das Problem, sondern verschlimmbesserst es.
Zuletzt geändert von jerch am Freitag 4. Mai 2012, 12:44, insgesamt 1-mal geändert.
patmaster
User
Beiträge: 106
Registriert: Donnerstag 3. Februar 2011, 17:21

Hyperion hat geschrieben:Dann sag uns doch aber mal, *wann* (bzw *wo* im Code) dieses Zeichen auftritt. Oben hast Du uns einen Datensatz gezeigt, in dem das Zeichen enthalten war - das ist also kein DB-Auszug, sondern ein String, der später im Code entstanden ist?
Das ensteht schon beim einlesen, aus dem Inputfile.
patmaster
User
Beiträge: 106
Registriert: Donnerstag 3. Februar 2011, 17:21

patmaster hat geschrieben:
Hyperion hat geschrieben:Dann sag uns doch aber mal, *wann* (bzw *wo* im Code) dieses Zeichen auftritt. Oben hast Du uns einen Datensatz gezeigt, in dem das Zeichen enthalten war - das ist also kein DB-Auszug, sondern ein String, der später im Code entstanden ist?
Das ensteht schon beim einlesen, aus dem Inputfile.

@jerch: Danke, ich werd mich da mal umschauen...
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

patmaster hat geschrieben: Das ensteht schon beim einlesen, aus dem Inputfile.
Hu? Ich dachte das kommt aus der DB? Also liest Du das XML doch aus Dateien? Und was heißt "entsteht" - steht das schon in der Datei drin? Oder taucht es erst *nach* dem Parsen auf?

Naja, jerch hat Dir wohl den helfenden Hinweis schon gegeben. Dennoch solltest Du Dich in Zukunft um mehr Präzision bei Deinen Beschreibungen bemühen ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
patmaster
User
Beiträge: 106
Registriert: Donnerstag 3. Februar 2011, 17:21

Hyperion hat geschrieben:
patmaster hat geschrieben: Das ensteht schon beim einlesen, aus dem Inputfile.
Hu? Ich dachte das kommt aus der DB? Also liest Du das XML doch aus Dateien? Und was heißt "entsteht" - steht das schon in der Datei drin? Oder taucht es erst *nach* dem Parsen auf?

Naja, jerch hat Dir wohl den helfenden Hinweis schon gegeben. Dennoch solltest Du Dich in Zukunft um mehr Präzision bei Deinen Beschreibungen bemühen ;-)

Ich lese fies ein und füge Infos hinzu die aus einer DB kommen. Der Part des Outputfiles der aber dann das Zeichen enthält kommst nicht aus der DB. Wie gesagt, das Zeichen ist im Inputfile im Attribut und wird dann vor das Element gesetzt beim Einlesen...warum auch immer...

Sry falls ich mich unverständlich ausgedrückt habe...
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@patmaster:
Schnelle Abhilfe des Problemes wäre, alle Attributewerte als CDATA zu deklarieren. Dann versucht er nicht mehr, das aufzulösen. Du sagtest zwar, es gibt eine DTD, nur sehe ich nicht, wo Du diese im Code einsetzt. Wenn die DTD sauber deklariert ist, sollte das Problem damit schon verschwinden, ansonsten müßtest Du die halt entsprechend erweitern.

Wenn Du die HTML-Enitäten nicht brauchst, kannst Du die auch einfach dauerhaft ersetzen (hat sma oben schon geschrieben).

Edit:
Die Definitionen der HTML-Entitäten für XHTML findest du hier: http://www.w3.org/TR/xhtml1/dtds.html#a ... characters
patmaster
User
Beiträge: 106
Registriert: Donnerstag 3. Februar 2011, 17:21

Code: Alles auswählen

load_dtd=True 
...und die Welt ist in Ordnung :)

Danke an alle !
Antworten