A szövegek megtisztítása, a lényeges információk szétválasztása a lényegtelen elemektől minden számítógépes szövegfeldolgozás alapja. Ezt a folyamatot hívjuk előfeldolgozásnak. Mostani írásomban ennek két konkrét esetet, esettanulmányokat közlök, továbbá végig utalok a szövegkódolási ajánlások előnyeire. Mind a feldolgozandó szöveg, mind a feldolgozott szöveg, az eredmény kódolásánál mekkora segítség tud lenni egy-egy ajánlás felhasználása.
Bármilyen számítógépes nyelvészeti szövegfeldolgozásra szánjuk rá magunkat az első lépés a szöveg megtisztítása és egységre hozása. Erre kívánok most két példát mutatni Python nyelven. József Attila 1926-os verseit fogjuk a Magyar Elektronikus Könyvtárból megszerezni. Mindezt úgy, hogy a már kódolt információkat ne veszítsük el. Továbbá a Digitális Irodalmi Akadémia anyagaiból fogunk egy második példát venni, hogy egy jól szerkesztett dokumentum mekkora segítséggel bír az előfeldolgozás során.
A legegyszerűbb mód
A legegyszerűbb módja, ahogy ezzel a folyamattal gyorsan végezhetünk, ha az NLTK egy segédfüggvényét használjuk. A clean_html() függvény automatikusan megtisztítja az összes HTML tag-et. Ezzel megkapjuk a teljes szöveget. Készen is állunk a kapott adatokat feldolgozására.
A módszer egyszerűségében rejlik a hátránya. A segédfüggvény nincs tekintettel a szövegben kódolt plusz információkra. Amire bizony, nekünk szükségünk van. Az ömlesztett szöveg ritkát használható fel valamire. Pontosítva: nagyobb munkát ró ránk, mert az ömlesztett szövegből már sokkal nehezebb lesz újra kinyerni a számunkra szükséges, a későbbi elemzést rossz irányba manipuláló adatokat.
A megőrzendő információk
A konkrét példákban a verseknek feldolgozásában két információt szeretnénk megkülönböztetni. Megkülönböztetni, de utalni, hogy az adatok összetartoznak. Tehát nem különálló két információhalmazt létrehozni, hanem végig utalni az összetartozásukra.
TEI-XML szövegkódolási kezdeményezés
Említettem a szövegkódolási ajánlásokat. Magyarországon egyre nagyobb népszerűségnek örvend a TEI-XML kezdeményezés. Mind a nyelvészeti, mind a textológiai kutatásokban elkezdték használni. Szegeden például bekerült a régi magyar irodalom szakirány törzsanyagába az ajánlás ismertetése és felhasználása. (Ezt a folyamatot nagyban segítette, hogy a Eszterházy Károly Főiskolán folyó Antikvakorpusz felállításában is ezt az ajánlást használják.)
De miért ajánlás? És mire jó valójában egy ajánlás betartása? Az ajánlás és a szabvány közti fogalmi különbségből adódik, hogy az ajánlásokat nem szükséges használni. Jólformált dokumentumokat kapunk az ajánlások mellőzése esetén is. Ezzel szemben a szabványok betartásának hiánya szintaxishibához vezet. A TEI-XML nevében lévő XML a szabvány, míg a TEI előtag utal az kezdeményezésre. Bővebben az ajánlásról a Parádi Andrea előadását érdemes elolvasni. Aki az XML alapjai is érdekel, annak pedig a Bíró Szabolcs Tankönyvtárban megtekinthető Szövegfeldolgozás XML alapokon könyve TEI ajánlásra vonatkozó részt, és az egész könyvet tudom ajánlani.
A Digitális Irodalmi Akadémia felhasználta a TEI-XML ajánlásokat. Magyarosították, így nem az egy-az-egyben a megfeleltetés, de mindenképp jól szerkesztett szövegről van szó abban az értelemben is, hogy könnyebb vele dolgozni. Ezzel a szemben a vizsgált József Attila szöveg szintén jólformált szövegnek minősül, de nem jól szerkesztett abban az értelemben, hogy nem egységes a szövegek kódolása. A konkrét elemzések közben ez jobban ki fog domborodni. A József Attila szövegek feldolgozásához egy olyan kódra lesz szükségünk, ami csak egy, csak az adott szerző esetén felhasználható, mert a teljes József Attila munkássághoz ugyan ezt az eljárást használják. - Ellenben a DIA által feldolgozott művekkel. Ezt elegendő egyszer megírni, mert az összes szerző összes munkáját azonos eljárással kódolták. Tehát a különbség úgy foglalható össze könnyedén, hogy míg a MEK szövegállományainak kódolása nem egységes, így minden adott szerző esetén újra kell írnunk az előfeldolgozást, addig a DIA a TEI-XML szövegkódolási szabványt felhasználva minden szerzőjének szövegét azonos eljárással kódolták, így az egyik szerzőn működő forráskódunk más szerzők esetén is sikeresen futtatható.
Mielőtt rátérnék az esettanulmányokra szeretném megemlíteni, hogy bármely a szöveg kódolására, szerkesztésére vonatkozó megjegyzésem nem a szerzőket és nem a Magyar Elektronikus Könyvtárat vagy a Digitális Irodalmi Akadémiát illetik. A szöveg olvashatóságát az itt felmutatott esetleges hiányosságok vagy problémák, továbbá a forrásos kibocsájtásának fő célját, hogy ezek a szövegek mindenki által könnyen elérhetőek maradjanak: egy cseppet sem befolyásolják! A cikkben csak a gépi előfeldolgozás szempontjából vizsgáljuk a szerzőket és a forrásokat.
Amire szükségünk lesz...
Először is tisztáznunk kell, hogy nem konkrét XML fájlokkal fogunk dolgozni, hanem az ezekből generált HTML fájlokkal. Mivel a HTML fájlok egy korlátozottabb leíró nyelv, így főleg az attribútumokra fogunk támaszkodni. Az attribútumokban rejlő plusz megjelöléseket fogjuk használni.
Ahhoz, hogy felmérhessük a terepet, egy honlap forráskódját sokféle eszköz áll a rendelkezésünkre. Én a leginkább a Firebug-ot szoktam használni, de az egyszerű forráskód megtekintése parancs (Ctrl-U vagy Nézet → Forráskód megtekintése) is bőven elegendő a folyamathoz.
Továbbá szükségünk lesz három Python Modulra a példák futtatásához.
József Attila 1926-os költeményei
Az 1926-os versek forráskódját áttekintve azt tapasztalhatjuk, hogy a címek rendelkeznek egy olyan attribútummal, amelyből el lehet indulni. Ezek kigyűjtése nagyon könnyen elvégezhető a következő kóddal.
A címek már a kezünkben. Hogyan tovább? A következő lépés a hozzátartozó versszöveg kiválasztása. Különállóan kiszűrni a versszövegeket könnyű feladat főleg úgy, hogy a címeket már meg tudjuk különböztetni. A valódi nehéz feladat az, hogy ezeket ne külön-külön szűrjük, hanem figyelemmel legyünk az összetartozásukra. Arra, hogy a címhez adott bekezdések tartoznak.
A József Attila szöveg esetén gyorsan feltűnhet, hogy minden lényeges elem a paragrafus tag-en belül találhatunk. Ez egy fontos kapaszkodó. Ezek szerint érdemes először a paragrafusokat kiszűrni. A paragrafus kiszűrése után pedig már a szűkített keresési térben vizsgálni, hogy melyik elem melyikbe tartozik.
A kód működik. Először megkeresi az összes p taget, aztán ebben a szűkített listában osztályozza tovább az elemeket. Az eredményt végignézve egy problémánk mégis lehet. - Az első versszövegnek hiányzik a címe! Ennek okát a szöveg szerkesztettségében rejlik. Pontosítva az egységes szerkesztettség hiányából. Ha megfigyeljük az első címet (A Csapat), akkor azt látjuk, hogy ez az egyetlen cím a teljes szövegben, aminek XPATH elérése nem p[n]/table/tbody/tr/td/h3/a, hanem ezzel ellentétben body/table/tbody/tr/td/h3/a
Az egyetlen különbség paragrafus tag hiánya. Mivel már az első szűkítés során a paragrafusok alapján szűkítettük a vizsgálandó elemeket, így az első cím már az első szűrésből kimaradt! - Ez a hiányosságot végül egy kis trükkel oldottam meg. A trükk annyiból áll, hogy először kigyűjtöttem az összes címet. Ezt a listát megfordítottam, és a fordított listának az utolsó elemét mindig kivettem abban az esetben, ha a címfeltételeknek megfelelő paragrafust talált az algoritmus. Hozzátéve, hogy így még mindig nem tartalmazza az első címet a paragrafusokat kiszűrő eredménylista, de ezt az plusz elemet az algoritmus futása előtt betoldottam, így a számunkra szükséges listát fogjuk kapni, ahol nem hiányzik egyetlen általunk szükséges adat sem.
Egy jól szerkesztett dokumentum
Végül egy utolsó példával megmutatom, hogy egy jól kódolt dokumentum mennyivel leegyszerűsíti ezt a folyamatot.
Forráskód
A teljes forráskód elérhető a BitBucket repomban. A kód tartalmaz egy plusz függvényt, ami a József Attila verseket XML formátumban adja vissza. Ez a formátum nem tartja be a TEI-XML ajánlásokat, de jól példázza, hogy általában egy közös formátumra kell hoznunk minden egyes feldolgozott anyagot, hogy később a működő programunk esetében ne kelljen újabb előfeldolgozásokat eszközölnünk. Az előfeldolgozást csak egyszer kelljen elvégezni. Aztán az egységes formában tárolt dokumentumaink könnyedén és gyorsan elérhetőek maradnak alkalmazásaink számára.
A legegyszerűbb mód
A legegyszerűbb módja, ahogy ezzel a folyamattal gyorsan végezhetünk, ha az NLTK egy segédfüggvényét használjuk. A clean_html() függvény automatikusan megtisztítja az összes HTML tag-et. Ezzel megkapjuk a teljes szöveget. Készen is állunk a kapott adatokat feldolgozására.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from nltk import clean_html | |
from urllib2 import urlopen | |
html = urlopen('http://mek.niif.hu/00700/00707/html/vs192601.htm').read() | |
tisztitott_html = clean_html(html) | |
print tisztitott_html[:50] |
A módszer egyszerűségében rejlik a hátránya. A segédfüggvény nincs tekintettel a szövegben kódolt plusz információkra. Amire bizony, nekünk szükségünk van. Az ömlesztett szöveg ritkát használható fel valamire. Pontosítva: nagyobb munkát ró ránk, mert az ömlesztett szövegből már sokkal nehezebb lesz újra kinyerni a számunkra szükséges, a későbbi elemzést rossz irányba manipuláló adatokat.
A megőrzendő információk
A konkrét példákban a verseknek feldolgozásában két információt szeretnénk megkülönböztetni. Megkülönböztetni, de utalni, hogy az adatok összetartoznak. Tehát nem különálló két információhalmazt létrehozni, hanem végig utalni az összetartozásukra.
- A vers címe
- A vers szövege
TEI-XML szövegkódolási kezdeményezés
Említettem a szövegkódolási ajánlásokat. Magyarországon egyre nagyobb népszerűségnek örvend a TEI-XML kezdeményezés. Mind a nyelvészeti, mind a textológiai kutatásokban elkezdték használni. Szegeden például bekerült a régi magyar irodalom szakirány törzsanyagába az ajánlás ismertetése és felhasználása. (Ezt a folyamatot nagyban segítette, hogy a Eszterházy Károly Főiskolán folyó Antikvakorpusz felállításában is ezt az ajánlást használják.)
De miért ajánlás? És mire jó valójában egy ajánlás betartása? Az ajánlás és a szabvány közti fogalmi különbségből adódik, hogy az ajánlásokat nem szükséges használni. Jólformált dokumentumokat kapunk az ajánlások mellőzése esetén is. Ezzel szemben a szabványok betartásának hiánya szintaxishibához vezet. A TEI-XML nevében lévő XML a szabvány, míg a TEI előtag utal az kezdeményezésre. Bővebben az ajánlásról a Parádi Andrea előadását érdemes elolvasni. Aki az XML alapjai is érdekel, annak pedig a Bíró Szabolcs Tankönyvtárban megtekinthető Szövegfeldolgozás XML alapokon könyve TEI ajánlásra vonatkozó részt, és az egész könyvet tudom ajánlani.
A Digitális Irodalmi Akadémia felhasználta a TEI-XML ajánlásokat. Magyarosították, így nem az egy-az-egyben a megfeleltetés, de mindenképp jól szerkesztett szövegről van szó abban az értelemben is, hogy könnyebb vele dolgozni. Ezzel a szemben a vizsgált József Attila szöveg szintén jólformált szövegnek minősül, de nem jól szerkesztett abban az értelemben, hogy nem egységes a szövegek kódolása. A konkrét elemzések közben ez jobban ki fog domborodni. A József Attila szövegek feldolgozásához egy olyan kódra lesz szükségünk, ami csak egy, csak az adott szerző esetén felhasználható, mert a teljes József Attila munkássághoz ugyan ezt az eljárást használják. - Ellenben a DIA által feldolgozott művekkel. Ezt elegendő egyszer megírni, mert az összes szerző összes munkáját azonos eljárással kódolták. Tehát a különbség úgy foglalható össze könnyedén, hogy míg a MEK szövegállományainak kódolása nem egységes, így minden adott szerző esetén újra kell írnunk az előfeldolgozást, addig a DIA a TEI-XML szövegkódolási szabványt felhasználva minden szerzőjének szövegét azonos eljárással kódolták, így az egyik szerzőn működő forráskódunk más szerzők esetén is sikeresen futtatható.
Mielőtt rátérnék az esettanulmányokra szeretném megemlíteni, hogy bármely a szöveg kódolására, szerkesztésére vonatkozó megjegyzésem nem a szerzőket és nem a Magyar Elektronikus Könyvtárat vagy a Digitális Irodalmi Akadémiát illetik. A szöveg olvashatóságát az itt felmutatott esetleges hiányosságok vagy problémák, továbbá a forrásos kibocsájtásának fő célját, hogy ezek a szövegek mindenki által könnyen elérhetőek maradjanak: egy cseppet sem befolyásolják! A cikkben csak a gépi előfeldolgozás szempontjából vizsgáljuk a szerzőket és a forrásokat.
Amire szükségünk lesz...
Először is tisztáznunk kell, hogy nem konkrét XML fájlokkal fogunk dolgozni, hanem az ezekből generált HTML fájlokkal. Mivel a HTML fájlok egy korlátozottabb leíró nyelv, így főleg az attribútumokra fogunk támaszkodni. Az attribútumokban rejlő plusz megjelöléseket fogjuk használni.
Ahhoz, hogy felmérhessük a terepet, egy honlap forráskódját sokféle eszköz áll a rendelkezésünkre. Én a leginkább a Firebug-ot szoktam használni, de az egyszerű forráskód megtekintése parancs (Ctrl-U vagy Nézet → Forráskód megtekintése) is bőven elegendő a folyamathoz.
Továbbá szükségünk lesz három Python Modulra a példák futtatásához.
- NLTK, ezt már több cikkünk is szól, például a Hogyan kezdtem szófajelemzőt írni
- BeautifulSoup, ez kifejezett HTML vagy XML állományok feldolgozásához írt modul. Telepítése easy_install BeautifulSoup lehetséges.
- Lxml, az xml állomány előállításához és a feldolgozásához.
József Attila 1926-os költeményei
Az 1926-os versek forráskódját áttekintve azt tapasztalhatjuk, hogy a címek rendelkeznek egy olyan attribútummal, amelyből el lehet indulni. Ezek kigyűjtése nagyon könnyen elvégezhető a következő kóddal.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from urllib2 import urlopen | |
from BeautifulSoup import BeautifulSoup | |
class Tisztitas(object): | |
def __init__(self, url): | |
self.url = url | |
def megnyit(self): | |
return urlopen(self.url) | |
def beolvasKodolva(self): | |
i = self.megnyit().read() | |
return BeautifulSoup(i.decode('latin-1')) | |
def cimekListaja(self): | |
i = self.beolvasKodolva() | |
return i.findAll('a', attrs={'name' : True}) | |
if __name__ == '__main__': | |
a = Tisztitas('http://mek.niif.hu/00700/00707/html/vs192601.htm') | |
a.cimekListaja() |
A címek már a kezünkben. Hogyan tovább? A következő lépés a hozzátartozó versszöveg kiválasztása. Különállóan kiszűrni a versszövegeket könnyű feladat főleg úgy, hogy a címeket már meg tudjuk különböztetni. A valódi nehéz feladat az, hogy ezeket ne külön-külön szűrjük, hanem figyelemmel legyünk az összetartozásukra. Arra, hogy a címhez adott bekezdések tartoznak.
A József Attila szöveg esetén gyorsan feltűnhet, hogy minden lényeges elem a paragrafus tag-en belül találhatunk. Ez egy fontos kapaszkodó. Ezek szerint érdemes először a paragrafusokat kiszűrni. A paragrafus kiszűrése után pedig már a szűkített keresési térben vizsgálni, hogy melyik elem melyikbe tartozik.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from urllib2 import urlopen | |
from BeautifulSoup import BeautifulSoup | |
class Tisztitas(object): | |
def __init__(self, url): | |
self.url = url | |
def megnyit(self): | |
return urlopen(self.url) | |
def beolvasKodolva(self): | |
i = self.megnyit().read() | |
return BeautifulSoup(i.decode('latin-1')) | |
def cimekListaja(self): | |
i = self.beolvasKodolva() | |
return i.findAll('a', attrs={'name' : True}) | |
def cimekEsSzoveg(self): | |
cimek = self.cimekListaja() | |
for i in self.beolvasKodolva().findAll('p'): | |
if i.find('a'): | |
print 'cim', i.text | |
else: | |
print 'szoveg', i.text | |
if __name__ == '__main__': | |
a = Tisztitas('http://mek.niif.hu/00700/00707/html/vs192601.htm') | |
a.cimekEsSzoveg() |
A kód működik. Először megkeresi az összes p taget, aztán ebben a szűkített listában osztályozza tovább az elemeket. Az eredményt végignézve egy problémánk mégis lehet. - Az első versszövegnek hiányzik a címe! Ennek okát a szöveg szerkesztettségében rejlik. Pontosítva az egységes szerkesztettség hiányából. Ha megfigyeljük az első címet (A Csapat), akkor azt látjuk, hogy ez az egyetlen cím a teljes szövegben, aminek XPATH elérése nem p[n]/table/tbody/tr/td/h3/a, hanem ezzel ellentétben body/table/tbody/tr/td/h3/a
Az egyetlen különbség paragrafus tag hiánya. Mivel már az első szűkítés során a paragrafusok alapján szűkítettük a vizsgálandó elemeket, így az első cím már az első szűrésből kimaradt! - Ez a hiányosságot végül egy kis trükkel oldottam meg. A trükk annyiból áll, hogy először kigyűjtöttem az összes címet. Ezt a listát megfordítottam, és a fordított listának az utolsó elemét mindig kivettem abban az esetben, ha a címfeltételeknek megfelelő paragrafust talált az algoritmus. Hozzátéve, hogy így még mindig nem tartalmazza az első címet a paragrafusokat kiszűrő eredménylista, de ezt az plusz elemet az algoritmus futása előtt betoldottam, így a számunkra szükséges listát fogjuk kapni, ahol nem hiányzik egyetlen általunk szükséges adat sem.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from urllib2 import urlopen | |
from BeautifulSoup import BeautifulSoup | |
class Tisztitas(object): | |
def __init__(self, url): | |
self.url = url | |
def megnyit(self): | |
return urlopen(self.url) | |
def beolvasKodolva(self): | |
i = self.megnyit().read() | |
return BeautifulSoup(i.decode('latin-1')) | |
def cimekListaja(self): | |
i = self.beolvasKodolva() | |
return i.findAll('a', attrs={'name' : True}) | |
def cimekSzoveggel(self): | |
cimek = self.cimekListaja() | |
cimek.reverse() | |
print cimek.pop() | |
for i in self.beolvasKodolva().findAll('p'): | |
if i.find('a'): | |
print i.text | |
else: | |
print i.text | |
if __name__ == '__main__': | |
a = Tisztitas('http://mek.niif.hu/00700/00707/html/vs192601.htm') | |
a.cimekSzoveggel() | |
Egy jól szerkesztett dokumentum
Végül egy utolsó példával megmutatom, hogy egy jól kódolt dokumentum mennyivel leegyszerűsíti ezt a folyamatot.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from urllib2 import urlopen | |
from BeautifulSoup import BeautifulSoup | |
class Tisztitas(object): | |
def __init__(self, url): | |
self.url = url | |
def megnyit(self): | |
return urlopen(self.url) | |
def beolvasKodolva(self): | |
i = self.megnyit().read() | |
return BeautifulSoup(i.decode('latin-1')) | |
if __name__ == '__main__': | |
a = Tisztitas('http://dia.jadox.pim.hu/jetspeed/displayXhtml?docId=0000000011&secId=0000000707&mainContent=true&mode=html#WORD_1_1') | |
html = a.beolvasKodolva() | |
for i in html.findAll('div', attrs={'class' : 'vers'}): | |
if i.find('div', attrs={'class' : 'cim'}): | |
print 'cim', i.text |
Forráskód
A teljes forráskód elérhető a BitBucket repomban. A kód tartalmaz egy plusz függvényt, ami a József Attila verseket XML formátumban adja vissza. Ez a formátum nem tartja be a TEI-XML ajánlásokat, de jól példázza, hogy általában egy közös formátumra kell hoznunk minden egyes feldolgozott anyagot, hogy később a működő programunk esetében ne kelljen újabb előfeldolgozásokat eszközölnünk. Az előfeldolgozást csak egyszer kelljen elvégezni. Aztán az egységes formában tárolt dokumentumaink könnyedén és gyorsan elérhetőek maradnak alkalmazásaink számára.
3 megjegyzés:
Az is igazán nagy kár, hogy a blogszolgáltatók (pl. a blog.hu) sem jelölnek semmiféle egységes jelölést a forrásban, például a cím vagy a szövegtörzs jelölésére -- pedig ez igazán nem kerülne semmibe.
Mondjuk az is lehet, hogy egyszerűen nem akarják, hogy egyszerűen letölthető legyen nagy mennyiségű tiszta adat -- ez is érthető, talán.
(ahogy látom, a blogger-en sincs semmi ilyesmi -- vagy csak nem néztem elég figyelmesen?)
Valójában ez nem a blogszolgáltatóktól függ. Ez a blog szerzőin múlik. Ha az alapvető, a felkínált témákból választanak, akkor egységes marad a blog szerkezete (a selectorok szerkezete). Viszont mindkét szolgáltató (Blog.hu vagy a Blogger) lehetőséget nyújt egyedi, saját téma készítésére is. - A saját téma készítése során viszont nem szükséges ügyelni ezekre a hagyományokra.
Úgyhogy valójában az a helyzet, hogy akár a blog.hu-ra, akár a bloggerre is lehetne írni egy elemzőt, ami a selectorok alapon elemzi az adott blogokat, de csak a blogok egy részén fog helyesen üzemelni. Például a mi blogunk elemezhető lenne, mert az alaptémák közül választottunk.
Mindenesetre egy ilyen vállalkozáshoz valami más módszerhez érdemes nyúlni, de ezzel még én sem foglalkoztam.
Megjegyzés küldése