Generelle Frage zur Auswertung von XML-Dateien

Heyho liebe Forengemeinde,
Ich war hier vor laengerer Zeit mal aktiv (letzter Post so um 2006), habe dann allerdings mein Passwort ueber die Jahre vergessen und musste mich nun neu anmelden. :)
Im Laufe meines Indologie-Studiums habe ich den Weg wieder zu FreeBSD gefunden und im Moment stehe ich vor dem Problem, dass ich einige sehr große XML-Dateien habe, mit denen ich irgendwie fertig werden muss.
Allerdings fehlt mir das nötige Know-How dazu. Ich bin ein ziemlicher XML/Datenbankenneuling und fuehl mich deswegen etwas wie der Ochs vorm Berg.
In erster Linie bearbeite ich im Moment alte indische Sanskrit-Texte.
Die Dateien sind in nach TEI-P5 aufgebaut und sehen grob so aus:
Code:
 <body>
            <div n="1" type="chapter">
                <pb n="J 1"/>
                <head>Chapter 1</head>
                <note place="margin">[&#160;O edn 383-402 :: O tr. 87-93</note>
                <div n="1">
                    <head>1.1. Prologue</head>
                    <note place="margin">[&#160;O edn 383-384 :: O tr. 87</note>
                    <lg type="stanza">
                        <l>M1.01a/ manum ekāgram āsīnam abhigamya maharṣayaḥ |</l>
                        <l>M1.01c/ pratipūjya yathānyāyam idaṃ vacanam abruvan || 1 ||</l>
                    </lg>
                </div>
</body>
Das Sanskrit, was ich verarbeiten moechte, liegt zwischen <l> und </l>.
Auf der anderen Seite habe ich ein XML-File, in der Sanskrit-Woerter in getaggter Form vorliegen.
So ein Eintrag sieht dann wie folgt aus:
Code:
<f form="aMSitavadByas"><pa><na><abl/><pl/><neu/></na><kr><cj><prim/></cj><no><ppa/></no></kr></pa><pa><na><dat/><pl/><neu/></na><kr><cj><prim/></cj><no><ppa/></no></kr></pa><pa><na><abl/><pl/><mas/></na><kr><cj><prim/></cj><no><ppa/></no></kr></pa><pa><na><dat/><pl/><mas/></na><kr><cj><prim/></cj><no><ppa/></no></kr></pa><s stem="aMSitavat"/></f>
Das, was unter form="" zu finden ist ist im Prinzip ein Wort, was in der TEI-P5-Datei zwischen den <l>-Tags auftaucht. Die restlichen Tags sind Daten, die fuer meine Übersetzungsarbeit sehr wichtig sind. Diese Datenbank umfasst circa 70mb.
Im Moment gehe ich noch so vor, dass ich jedes einzelne Wort mit dem Editor herauspfluecke und dann ein
'grep -e' ausfuehre, um an den relevanten Eintrag zu kommen. Das ist natuerlich nicht besonders effizient und von der schoenen XML-Struktur mache ich so ja auch garkeinen Gebrauch.
Wie löse ich das nun eleganter? In welche Tools sollte ich mich einlesen, um hier vorwärts zu kommen?
Ich denke mal es ist keine Raketenwissenschaft. Bisher benutze ich Pythonscripte, um aus den TEI-Dateien Latex-Dateien zu machen und um einfach Konvertierungsgeschichten in den Strings durchzufuehren.
Diese Scripte habe ich allerdings nicht selbst geschrieben, sondern nur fuer meine Zwecke umgebogen.
Also was ich suche ist im Prinzip ein Script, was so einen String zwischen zwei <l>-Tags Wort fuer Wort trennt,
mit der Datenbank abgleicht, bei Uebereinstimmung den entsprechenden Eintrach hinter das Wort setzt und dann zum naechsten Wort geht und wieder nach einer Uebereinstimmung sucht. Da wird es fuer Python doch sicher etwas geben.
Ich bin fuer jeden Hilfe dankbar! (Eine Loesung erwarte ich natuerlich nicht :))
 
Also wenn du etwas Ad-Hoc suchst kann ich XPath oder XQuery empfehlen.

Mit einem Xpath query wie
Code:
//f[@form='blablubb']
würdest du dann das f element selektieren mit dem attribut from="blablubb".

Aber Ggf. willst du ja den gesamten Prozess automatisieren. Oder vielleicht ein HTML Dokument erstellen willst das bei einem mouse-Over eines Wortes die semantischen Infos anzeigt oder so. Dazu kenne ich dein Problem nicht gut genug.

Aber für die TEI Daten gibt es doch wenn ich mich recht erinnere einige gute tools von den verschiedenen Unis, hast du die schon angeschaut ?
 
Von Skripten aus kann man xsltproc benutzen (libxslt). Du musst nur wissen wie man Stylesheet-Transformationen erstellt. Hier ist XPath/XQuery gut zu gebrauchen. Es gibt sicherlich auch Module für Python welche XSLT ansteuern. Ich kenne mich mit Python aber nicht aus.
 
Hey, danke schonmal fuer die hilfreichen Antworten!
Ja es gibt auch Tools von den Unis, die sind aber mitunter recht komplex und gerade an unserer Uni wird das Thema Computerlinguistik gemieden wie die Pest. Hier sitzen die Profs noch mit edit und suchen dort per strg-F nach den Verbformen. :)
Im Moment fehlt mir einfach die Zeit mich in die Tools der anderen Unis einzuarbeiten, auch wenn es da sicher viel zu holen gibt. Aber vielleicht klappt das ja mal in den Sommerferien oder so.


Im Prinzip moechte ich, dass die XML-Datei mit dem Tag <l>brahmanam svanagaram gacchati</>
dann um eine Kopie dieses tags mit einem anderen Namen, nennen wir es mal <grammatik>brahmanam svanagaram gacchati</grammatik>
erweitert wird. Das habe ich mir auch schon mit einem XSLT-Stylesheet zusammengebastelt und das funktioniert
In dieser Kopie des Tags sollen dann die aus der Datenbank enthaltenen Eintraege eingesetzt werden.
Mache ich das jetzt auch mit XSLT/xsltproc oder benoetige ich dazu XPATH/XQuery?
Soweit ich mein Problem bisher verstanden habe (korrigiert mich bitte, wenn ich da falsch liege):
Der String muss von einer Python-Funktion eingelesen und per regexp in Woerter getrennt werden, die dann jeweils per XPATH aus der Datenbank gefischt werden und dann entsprechend eingefuegt werden. wenn eine Uebereinstimmung vorliegt.
Was ich also brauche ist ein rudimentaeres Verstaendnis von Python/regexp und xpath?
Das Ergebnis sollte dann ungefaehr so aussehen:
Code:
<lg>
    <l>naras nagaram gacCati</l>
</lg>
<gram>
    <l>
        <f form="naras"><na><nom/><sg/><mas/></na><s stem="nara"/></f>
        <f form="nagaram"><na><acc/><sg/><mas/></na><na><acc/><sg/><neu/></na><na><nom/><sg/><neu/></na><s stem="nagara"/></f>
        <f form="gacCati"><v><cj><prim/></cj><sys><prs k="1"><md><pr/></md><para/></prs></sys><np><sg/><trd/></np></v><s stem="gam"/></f>
    </l>                     
</gram>

Dieser Code wird dann wiederum von einem Python-Script eingelesen und zu in Latex umgewandelt. Fuer diesen Part habe ich auch schon ein Script, was seinen Dienst ganz ordentlich tut. Es fehlt also nur ein Zahnrad im Getriebe. ;)
 
Also so etwas einfaches wie Trennen von Wörtern müsste so ein XSLT-Prozessor noch hinkriegen, aber Lookups in der Datenbank muss man wohl selbst machen und sich eine Strategie ausdenken wie man es im gleichen XML-Dokument verschmilzt.

Die Frage wie man es am besten macht hängt aber auch mitunter davon ab, ob das ganze Verfahren, welches Du beschreibst einmalig oder dynamisch wiederholend ist. Wenn Du es nur einmalig so ergänzen musst, dann ist da nicht viel zu tun, man kann vieles dreckig lösen (ohne viel Aufwand hineinzustecken). Aber wenn das so etwas wie ein Web-Dienst ist, wo immer wieder Anfragen die richtigen Antworten generieren müssen, dann muss man es ordentlich machen und bis zum letzten Rädchen zu einer geschlossenen Funktion zusammenschnüren.
 
Zur Not python mit etree :)

Ich hab da mal:

Code:
from lxml import etree

def get_word_semantics(word):
    return etree.Element('fakewordSemantics')


doc = etree.parse("dateipfad")
for lg in doc.xpath('//lg'):
    grammar = etree.Element('grammar')
    parent = lg.getparent()
    parent.insert(parent.index(lg) + 1, grammar)
    for l in lg.iter(tag="l"):
        gl = etree.Element('l')
        grammar.append(gl)
        for word in l.text.split():
            f = get_word_semantics(word)
            gl.append(f)
           
print etree.tounicode(doc, pretty_print=True)
 
OK super, das sieht schonmal sehr konkret aus. Vielen Dank. :)
Nach einem langen Vormittag der Fummelei bin ich jetzt bei folgendem angelangt:
Code:
from lxml import etree

#Funktion, die in der Datenbank SL_roots nach dem Wort, das als Parameter angegeben ist, sucht
database = etree.parse("../database/SL_roots.xml")
def get_word_semantics( begriff ):
  return etree.tostring(database.find("//f[@form='%s']" % begriff))


doc = etree.parse("test.xml")
for lg in doc.xpath('//lg'):
  grammar = etree.Element('grammar')
  parent = lg.getparent()
  parent.insert(parent.index(lg) + 1, grammar)
  for l in lg.iter(tag="l"):
  gl = etree.Element('l')
  grammar.append(gl)
  for word in l.text.split():
  f = get_word_semantics(word)
  gl.append(f)
   
print etree.tounicode(doc, pretty_print=True)
Der haengt sich im Moment allerdings noch bei dieser Stelle auf:
Code:
line 13, in <module>
  parent.insert(parent.index(lg) + 1, grammar)
AttributeError: 'NoneType' object has no attribute 'insert'
Irgenwdelche Ideen dazu? Mir raucht langsam der Kopf...
 
Wenn ein <lg> als oberstes Element in der Datei ist, klappt das .getparent() natürlich nicht.
Sonst habe ich auf den ersten Blick keine Idee.
Die Einrückung ist in deinem Post leider kaputtgegangen.

Und Denk dran, ggf. musst du einige Wörter ausfiltern, diese Aufzählungsdinger wie M.1.01a oder so. Und ich nehme an, dass du ggf. Word stemming machen musst damit du das wort auch findest. Oder musst du den Fall behandeln, wo du kein Eintrag in der Datenbank findest.
 
Danke, ich hab natuerlich den Fehler gehabt und das <body>-Tag aus meiner Datei rauseditiert.
jetzt rennt mein Script schonmal und tut genau das, was ich moechte. merci!
Im Moment laeuft das hier:
Code:
from lxml import etree

#Funktion, die in der Datenbank SL_roots nach dem Wort, das als Parameter angegeben ist, sucht
database_roots = etree.parse("../database/SL_roots.xml")
database_adverbs = etree.parse("../database/SL_adverbs.xml")
database_final = etree.parse("../database/SL_final.xml")
database_nouns = etree.parse("../database/SL_nouns.xml")
database_pronouns = etree.parse("../database/SL_pronouns.xml")
#database_parts = etree.parse("../database/SL_parts.xml")
def get_word_semantics( begriff ):
  if database_roots.find("//f[@form='%s']" % begriff) != None:
  return database_roots.find("//f[@form='%s']" % begriff)
  elif database_adverbs.find("//f[@form='%s']" % begriff) != None:
  return database_adverbs.find("//f[@form='%s']" % begriff)
  elif database_final.find("//f[@form='%s']" % begriff) != None:
  return database_final.find("//f[@form='%s']" % begriff)
  elif database_nouns.find("//f[@form='%s']" % begriff) != None:
  return database_nouns.find("//f[@form='%s']" % begriff)
  elif database_pronouns.find("//f[@form='%s']" % begriff) != None:
  return database_pronouns.find("//f[@form='%s']" % begriff)
#  elif database_parts.find("//f[@form='%s']" % begriff) != None:
#  return database_parts.find("//f[@form='%s']" % begriff)
  else:
  return etree.Element('none')

parser = etree.XMLParser(remove_blank_text=True)
doc = etree.parse("test.xml")
for lg in doc.xpath('//lg'):
  grammar = etree.Element('grammar')
  parent = lg.getparent()
  parent.insert(parent.index(lg) + 1, grammar)
  for l in lg.iter(tag="l"):
  gl = etree.Element('l')
  grammar.append(gl)
  for word in l.text.split():
  f = get_word_semantics(word)
  gl.append(f)
   
output = etree.tounicode(doc, pretty_print=True)
#some markup replacements
output = output.replace('grammar>', 'grammar>\n')
output = output.replace('</grammar>', '\n</grammar>')
output = output.replace('<none/>', '<none/>\n')

print(output)
Noch eine letzte Frage, bevor fuer heute Feierabend ist:
Mitunter sind die Datenbanken, die ich einlese recht gross (SL_parts.xml z.B. 70mb) und wenn ich sie nicht auskommentiere, dann rennt er sich zu Tode.
Welche Moeglichkeiten gibt es, dieses Problem zu umgehen? Hab schon daran gedacht die Dateien vllt. in ein memfs zu holen, aber gibt es da auch elegantere Loesungen fuer?
 
Das Problem ist, dass lxml bei jedem .xpath() aufruf eine lineare Suche macht, und das ist zu langsam.

Du müsstest beim starten alle wörter in den datenbanken in ein dictionary einlesen {'wort':"xml bla"}. dann muss er nicht jedes mal die 70mb durchlesen sondern hat es sofort.

Untested:
Code:
lookup = {}

for db_file in ['file1.xml', 'file2.xml']:
    for f in etree.parse(db_file).xpath('//f'):
        if 'form' in f.attrib:
            lookup[f.attrib['form']] = etree.tostring(f)

def lookup_word(begriff):
    if begriff in lookup:
        return etree.fromstring(lookup[begriff])
    else:
        return etree.Element('none')

!! Wichtig: wenn du ein Element nimmst und woanders einhängst, wird es aus dem alten Doc augehängt, deshalb musst du entweder eine "copy.deepcopy(elem)" machen oder es über strings machen. in meinem beispiel hier ist es schon gelöst.

Jetz aber mal selber dran :D Python lernen ist ne spaßige sache.
 
Zurück
Oben