Voraussetzung für diese Lektion ist die Lektion Vorverarbeitung von Texten.
Viele Fachgebiete haben ihre eigene Terminologie. Wir können diese Terminologie einfach finden durch die Frequenzen der Wörter in einer Sammlung mit Texten aus einem Fachgebiet mit den Wortfrequenzen in einem allgemeinen Corpus zu vergleichen. Außerdem sehen wir, wie wir Texte aus Wikipedia herunterladen und nutzen können.
Um das Verfahren zu zeigen, brauchen wir eine Sammlung mit Texten aus einem bestimmten Fachgebiet. Hierfür bietet sich die Wikipedia an. Wir können leicht eine Menge Texte aus einer Wikipediakategorie herunterladen. ALs Beispiel nehmen wir Wikipedia-Artikel aus der Kategorie Bibliothek.
Wikipedia hat eine RESTfull API, die wir zum herunterladen der Text benutzen können.
import requests
def getWikiText(title):
response = requests.get(
'https://de.wikipedia.org/w/api.php',
params={
'action': 'query',
'format': 'json',
'titles': title,
'prop': 'extracts',
'explaintext': True
}
).json()
page = next(iter(response['query']['pages'].values()))
if page != None and 'extract' in page:
return page['extract']
else:
return ''
Wir testen die oben stehende Funktion zum herunterladen des Textes einer Wikipedia-Seite:
testtext = getWikiText('Technische Informationsbibliothek')
print(testtext[:400]+'...')
Wir können auch die Liste aller Titel in einer Kategorie bekommen. Wie üblich bei Abfragen an Webservern mit möglicherweise langen Ergebnislisten, bekommen wir nur einen Teil der Ergebnisse und ein sogenanntes Resumption Token, mit der wir den nächsten Teil der Ergebnisse bekommen können.
def getWikiPages(category):
titles = []
response = requests.get(
'https://de.wikipedia.org/w/api.php',
params={
'action': 'query',
'list': 'categorymembers',
'cmtitle': 'Category:'+category,
'cmtype':'page',
'format': 'json'
}
).json()
#pprint.pprint(response)
while(response):
titles.extend( [t['title'] for t in response['query']['categorymembers']] )
if 'continue' in response:
cont = response['continue']['cmcontinue']
response = requests.get(
'https://de.wikipedia.org/w/api.php',
params={
'action': 'query',
'list': 'categorymembers',
'cmtitle': 'Kategorie:' + category,
'cmtype': 'page',
'cmcontinue': cont,
'format': 'json'
}
).json()
else:
break
if not cont:
response = None
return titles
Wir testen die Funktion:
getWikiPages('Militärbibliothek')
In ähnlicher Weise bekommen wir alle Unterkategorien einer Kategorie:
def unterkat(category):
titles = []
response = requests.get(
'https://de.wikipedia.org/w/api.php',
params={
'action': 'query',
'list': 'categorymembers',
'cmtitle': 'Kategorie:' + category,
'cmtype':'subcat',
'format': 'json'
}
).json()
while(response):
titles.extend( [t['title'][10:] for t in response['query']['categorymembers']] )
if 'continue' in response:
cont = response['continue']['cmcontinue']
response = requests.get(
'https://de.wikipedia.org/w/api.php',
params={
'action': 'query',
'list': 'categorymembers',
'cmtitle': 'Kategorie:' + category,
'cmtype': 'subcat',
'cmcontinue': cont,
'format': 'json'
}
).json()
else:
break
if not cont:
response = None
return titles
def unterkat_rekursiv(category):
result = []
todo = [category]
while(len(todo) > 0):
cat = todo.pop()
for sub in unterkat(cat):
if not sub in result:
result.append(sub)
todo.append(sub)
return result
Test:
unterkat_rekursiv('Bibliothek nach Typ')
Wir kombinieren alles und speichern alle heruntergeladenen Texte:
import codecs
import os
def save_text(dir,page_name, page_text):
file_name = page_name.replace(' ','_').replace('/','_')
f_out = codecs.open(dir + '/' + file_name+".txt", "w", "utf-8")
f_out.write(page_text)
f_out.close()
def save_all_texts(category,directory):
if not os.path.exists(directory):
os.makedirs(directory)
for cat in unterkat_rekursiv(category):
for title in getWikiPages(cat):
text = getWikiText(title)
try: #Speicern klappt nicht immer bei Sonderzeichen im Titel. Darum kümmern wir uns jetzt mal nicht.
save_text(directory,title,text)
except:
continue
save_all_texts('Bibliothek nach Typ','Corpora/Bibliothek')
Es sollten jetzt über 800 Texte gespeichert worden sein.
Wir nutzen die standard Vorverarbeitung der Texte und zählen die Lemmata. Um das zählen zu vereinfachen nutzen wir die Klasse Counter aus Collections
import glob
import nltk
import codecs
import treetaggerwrapper
from collections import Counter
tagger = treetaggerwrapper.TreeTagger(TAGLANG='de')
fdist = Counter()
filelist = glob.glob("Corpora/Bibliothek/*.txt")
for datei in filelist:
try:
textfile = codecs.open(datei, "r", "utf-8")
except:
continue
text = textfile.read()
textfile.close()
sentences = nltk.sent_tokenize(text,language='german')
sentences_tok = [nltk.word_tokenize(sent,language='german') for sent in sentences]
for sent in sentences_tok:
tags = tagger.tag_text(sent,tagonly=True)
tags = treetaggerwrapper.make_tags(tags);
for tag in tags:
fdist.update([tag.lemma])
Wir schauen uns die 10 häufigsten Wörter an:
print(fdist.most_common(10))
Diese Liste ist vokommen uninteressant. Wir sehen die folgende Probleme:
Wir lösen zunächst die ersten beide Problemen mit einer Verbesserung vom oben stehenden Code. Wir teilen den Text erst in Absätze auf und dann erst in Sätzen. Satzzeichen können leicht erkannt werden, da diese vom Treetagger ein Tag, das mit einem Dollarzeichen anfängt bekommen.
import re
fdist = Counter()
filelist = glob.glob("Corpora/Bibliothek/*.txt")
h_mark = re.compile(r'==+')
leerzeilen = re.compile(r'\n+')
nrOfWords = 0
for datei in filelist:
try:
textfile = codecs.open(datei, "r", "utf-8")
except:
continue
text = textfile.read()
textfile.close()
text = h_mark.sub('\n\n',text)
leerzeilen = re.compile(r'\n+')
paragraphs = leerzeilen.split(text)
for par in paragraphs:
sentences = nltk.sent_tokenize(par,language='german')
sentences_tok = [nltk.word_tokenize(sent,language='german') for sent in sentences]
for sent in sentences_tok:
tags = tagger.tag_text(sent,tagonly=True)
nrOfWords += len(tags)
words = [tag.lemma for tag in treetaggerwrapper.make_tags(tags) if not tag.pos[0] == "$"]
fdist.update(words)
print(fdist.most_common(10))
Es ist nicht so interessant zu wissen, welche Wörter häufig in unserere Textsammlung vorkommen. ielmehr interessiert es uns, welche Wörter häufiger vorkommen als man es im allgemeinen erwarten würden. Hierzu müssen wir die Frequenzen in unserem Korpus mit Frequenezen in einem allgemeinen Korpus vergleichen. Hierfür nutzen wir eine Liste mit Wortfrequenzen der 100 000 häufigsten Wörter aus einer Sammlung verschiedener Korpora mit insgesamt 7 Milliarden Wörtern des Instituts für Deutsche Sprache in Mannheim. Die Liste und Informationen dazu finden Sie auf der Webseite des IDS: http://www1.ids-mannheim.de/kl/projekte/methoden/derewo.html
refdist = nltk.FreqDist()
nrOfWordsRef = 0
freqdata = codecs.open("Corpora/DeReKo-2014-II-MainArchive-STT.100000.freq", "r", "utf-8")
for line in freqdata:
(word,lemma,pos,freq) = line.split('\t')
nrOfWordsRef+= float(freq)
refdist[lemma] = float(freq)
freqdata.close()
Um die Frequenzen einigermaßen vergleichbar zu machen, multiplizieren wir die Frequenzen aus unseren Daten mit einer Konstante, die wir wie folgt ermitteln. Genau genommen brauch wir diese Konstante nicht, da die Wörter, die wir finden, hierdurch nicht anders werden.
c = refdist['es']/fdist['es']
print(c)
Schließlich berechnen wir die relative Frequenzen. Damit die Zahlen besser lesbar sind, nehmen wir den Logarithmus der relativen Frequenz. Wir sortieren die Liste und geben die 100 häufigsten Wörter aus:
import math
import pprint
word_rel_freq = {lemma:math.log(c * fdist.get(lemma) / refdist.get(lemma,1)) for lemma in fdist }
word_rel_freq = sorted(word_rel_freq.items(), key=lambda x: x[1],reverse=True)
pprint.pprint(word_rel_freq[0:100])
Abgesehen von einigen Ergebnissen, die auf eine unterschiedliche Lemmatisierung zurückgführt werden können, finden wir tatsächlich nur Wörter, die dem Bibliotheksbereich zugeordnet werden können.
Die relative Frequenz stellt sich so als ein sehr effektive Maß, um Fachvokabular zu erkennen, heraus.
Um tatsächlich alles Fachspezifische Begriffe zu erkennen, müssten wir auch noch Mehrwortausdrücke erkennen und vergleichen. Viele Fachbegriffe werden nicht durch ein Wort, sondern durch eine kurze Nominalphrase ausgedrückt, wie 'Open Access', 'elektronisches Dokument', 'Institut für Weltwirtschaft'. Diese Wortfolgen zu finden ist nicht besonders schwierig. Da diese Wörter in den meisten Freqenzlisten nicht erhalten sind, müsste man die Referenzfrequenzen auch selber ermitteln.