Nutzung von ConGen
Das folgende Kapitel ist
ein Auszug aus dem Bericht zum Laborpraktikum „ConGen
– Dynamische Content-Erweiterung von Webseiten“ von Nicole Mencke
und Steffen Kernchen am Institut für Verteilte
Systeme an der Otto-von-Guericke-Universität
Magdeburg. Im weiteren Verlauf wird der grundsätzliche Umgang mit dem ConGen-System erläutert. Dabei wird zuerst auf die Nutzung der
graphischen Benutzeroberflächen und danach auf das Erstellen eigener Parser
eingegangen.
5.1 Die
Oberflächen
Für eine problemlose
Nutzung des ConGen-Systems wurden die GUI's ergonomisch gestaltet und die einzelnen logischen
Bereiche klar voneinander differenziert. Für das Arbeiten mit dem System wurde
ein Programminterface zum Arbeiten mit den Parsern und ein
Datenbearbeitungsinterface bereitgestellt. Der Umgang mit ihnen wird in den nächsten
Abschnitten erläutert.
5.1.1
Programminterface
Die in Abbildung 1
dargestellte GUI dient zum Verwalten der Parser und zum Einleiten des Parsens.

Abbildung 1
Programminterface
Der obere Bereich der Oberfläche
dient der ersten erwähnten Aufgabe. Über die Benutzung des Buttons '1' können
neue Parser dem System hinzugefügt werden. Name und Standort auf dem Rechner
werden dabei vom System gespeichert. Sie stehen dem System auch nach
wiederholtem Start zur Verfügung und müssen nicht erneut integriert werden. Button
'2' dient dem Löschen nicht mehr benötigter Parser. Dafür wird im Bereich 'A' der
GUI der entsprechende Parser markiert und das Entfernen über das Betätigen
dieses Buttons eingeleitet. Dabei wird nur die Referenz auf diesen Parser im ConGen-System gelöscht und nicht die Datei auf dem Rechner.
Der untere Bereich des Programminterfaces dient dem eigentlichen Vorgang des
Parsens. Dafür werden im Bereich 'B' die entsprechenden Parser markiert. Über
den Button '5' wird das Parsen mit den markierten
Parsern eingeleitet. Als Ergebnis wird eine XML- Datei mit den extrahierten
Informationen erzeugt. Mit Hilfe des Buttons '3' können diese Informationen
manuell kontrolliert, geändert und erweitert werden. Dafür öffnet dieser Button
das Datenbearbeitungsinterface, welches im nächsten Abschnitt näher beschrieben
wird. Button '4' dient der Darstellung der XML-Datei
in einem vorher spezifizierten Browser. Ist noch kein Browser angegeben,
erscheint ein Dialog, in welchem der Standort des Browsers auf dem Rechner
angegeben werden kann. Der EXIT-Button '6' terminiert
das ConGen-System.
5.1.2
Datenbearbeitungsinterface

Abbildung 2
Datenbearbeitungsinterface
Das
Datenbearbeitungsinterface wurde ebenfalls für einen intuitiven Umgang mit dem System
entwickelt. Es erlaubt die Kontrolle, die Änderung und das Hinzufügen bzw. Löschen
von Konferenzdaten. Im linken Bereich 'A' der Oberfläche werden die extrahierten
Informationen der jeweiligen Konferenz präsentiert. Zum Wechsel der Darstellung
zwischen den Konferenzen dienen die Buttons, die unter '6' zu finden sind.
Diese Buttons haben unterschiedliche Darstellungsmodi, um dem Nutzer intuitiv
den Beginn und das Ende der Konferenzliste anzuzeigen. Neue Konferenzen können über
den Button '1' hinzugefügt werden. Die aktuell angezeigte Konferenz kann über
den Button '2' gelöscht werden. Diese und alle weiteren Änderungen werden erst
durch das Betätigen der Buttons '3' oder '4' endgültig wirksam. Button '5'
schließt die aktuelle GUI, ohne die bis dato getätigten Änderungen in die XML-Datei zu übernehmen.
5.2
Erstellen eigener Parser
Die Erstellung eigener
Parser ist relativ simpel. Die Vorgehensweise ist vorgegeben und wird im
weiteren Verlauf detailliert am Beispiel eines Parsers für die
Seite "http://www.multiagent.com/Conferences/Related
Conferences/index.html" beschrieben. Der
allgemeine Vorgang des Parsens ist dem Bild 3 zu entnehmen.

Abbildung 3
Interaktionsdiagramm: Parsen
5.2.1
Erste Schritte
Vor der Implementation der notwendigen Methoden des Parsers sind
einige Punkte zu beachten. Jeder Parser, der für das ConGen-System
entwickelt wird, ist vom Interface ConGenParserInterface abzuleiten. So wird
sichergestellt, dass der Parser alle notwendigen Methoden enthält und die
ermittelte Datenstruktur korrekt erzeugt wurde. Es werden vom ConGen-System nur Parser akzeptiert, die dieses Interface
implementieren.
Zum besseren Verständnis
des Interfaces wurde aus ihm eine Referenzklasse erstellt. Jeder ConGen-Parser enthält eine Reihe von globalen, privaten
Variablen, die innerhalb des Konstruktors instantiiert bzw. initialisiert werden müssen. Dazu gehören:
5.2.2
Parsen des HTML-Quellcodes
Erster Ansatzpunkt der
Informationsextraktion ist, wie schon in 4.2.1 beschrieben, die Extraktion der
HTML-Tags aus dem HTML-Quellcode der gewünschten Seite mit der Methode parse(). Dafür wird zuerst ein
Parser des HTMLParser-Paketes mit der gewünschten URL
erzeugt:
Parser parser = new Parser(url, new DefaultParserFeedback());
Dieser erzeugt eine
listenartige Datenstruktur, in welcher die einzelnen extrahierten Tags des
Quellcodes, sogenannte Nodes, enthalten sind. Neben
vordefinierten Scannern, welche nach den gängigsten Tags suchen, besteht die Möglichkeit
eigene Scanner zu definieren. Für die normale Anwendung sollte dies aber nicht
notwendig sein. Mittels eines NodeIterators kann nun über diese
Datenstruktur iteriert werden. Sinn dieses Vorgehens ist
es, alle Nodes, die zu einer Konferenz gehören, in
einer NodeList zusammenzufassen. Sollte dies innerhalb dieser
Methode nicht vollständig möglich sein, so stehen Methoden innerhalb der Klasse
Wastebasket bereit. Eigene unterstützende Methoden sollten ebenfalls
gut kommentiert dieser Klasse hinzugefügt werden, um für zukünftige Parserentwicklungen
zur Verfügung zu stehen. Überflüssige Nodes, wie
solche, die Tags für das Einfügen von Bildern oder die Formatierung von Text
enthalten, können über die Methode clearEntrys(LinkedList)
aus der
Klasse Wastebasket aus den extrahierten NodeList entfernt werden.
Die Besonderheit der
verwendeten Beispielseite ist, dass die Informationen zu den einzelnen Seiten
als HTML-Listenelemente abgespeichert sind. So beginnt eine neue Node-List, sobald ein weiteres LI-Tag auftaucht. Die Node-Extraktion findet ihren Abschluss, sobald
die HTML-Listenstrukturen geschlossen werden. So werden nur Nodes
in die weitere Betrachtung einbezogen, die potentiell Informationen enthalten können.
Ergebnis dieser ersten
Methode ist eine LinkedList entrys von NodeLists
mit Text, der Informationen zu den einzelnen Konferenzen enthält. Diese Liste
wird global abgespeichert, um zur Weiterverarbeitung zur Verfügung zu stehen.
Hinweise:
5.2.3
Einleiten der Informationsextraktion
Die eigentliche
Informationsextraktion wird, wie das eben beschriebene
Parsen des HTML-Quelltextes, ebenfalls außerhalb des Parsers eingeleitet.
Dieses Vorgehen muss unbedingt eingehalten werden, damit aus dem ConGen-System auf den Parser zugegriffen werden kann. Die
im Folgenden beschriebene getContents()-Methode leitet die
Extraktion der einzelnen Informationen ein. Sie hat keine Parameter und gibt
eine LinkedList mit Conf-Objekten zurück. Diese Objekte
stellen die interne Datenstruktur zur Speicherung der Konferenzdaten dar. Im
Folgenden wird über die in der parse()-Methode erzeugte LinkedList iteriert und deren NodeLists werden mittels noch zu beschreibender Methoden die
Konferenzinformationen entzogen. Diese Informationen werden im Konferenzobjekt Conf gespeichert und dieses der
LinkedList contents hinzugefügt, die zurückgegeben
wird.
Die eben beschriebene
Methode kann ohne Änderung von allen ConGen-Parsern
implementiert werden. Bei den Extraktionsmethoden ist dabei auf eine korrekte
Belegung der zurückgegebenen Datenstrukturen zu achten, da hier die entsprechenden
Daten dem Conf -Objekt hinzugefügt werden. So muss beispielsweise
das Datum, bis zu welchem die Paper eingetroffen sein müssen, das Jahr 1900
aufweisen, wenn keine entsprechende Information zu extrahieren war. Darüber
wird die Belegung der booleschen Variablen noCFPDate geregelt, welche
unkompliziert Auskunft über das Vorhandensein des entsprechenden Datums gibt.
Hinweise:
5.2.4
Methode findShortName(NodeList)
Diese Methode dient der
Suche nach der Abkürzung des offiziellen Konferenznamens. Dafür wird der
Methode die zu durchsuchende NodeList als Parameter
mit übergeben. Die eigentliche Extraktionsarbeit wird von der Methode getPossibleShortNames(NodeList)
erledigt.
Sie liefert eine Liste mit möglichen Abkürzungen zurück. Unter Berücksichtigung
der Informationsstruktur der Webseite wird die erste gefundene mögliche Abkürzung
zurückgegeben und im entsprechenden Conf -Objekt abgespeichert. Kerngedanke
dieser Informationsextraktion in der Methode getPossibleShortNames(NodeList)
ist, dass
bestimmte Wörter mögliche Abkürzungen sind, wenn sie mindestens zwei Großbuchstaben
enthalten. Der Methodenablauf lässt sich kurz wie folgt skizzieren:
5.2.5
Methode findLongName(NodeList, String)
findLongName(NodeList,
String) dient
der Extraktion des vollständigen Namens der Konferenz. Die Methode hat zwei Parameter,
die zu durchsuchende NodeList und die bereits
extrahierte Abkürzung des Konferenznamens. Zurückgegeben wird der komplette Name
der Konferenz, falls einer im HTML-Quellcode angegeben war. Hier zeigt sich ein
weiterer Grund, warum der Ablauf der getContents()-Methode nicht geändert werden
sollte. Ohne eine bereits extrahierte Abkürzung ist das Auffinden des ganzen Konferenznamens
nach dem hier beschriebenen Muster nicht
möglich. Natürlich sei es dem Nutzer freigestellt, einen eigenen Weg zum
Extrahieren des Konferenznamens zu implementieren. In diesem Fall sollte ein
leerer String als Parameter übergeben werden, um die Konsistenz des Parsers mit
dem ConGenParserInterface zu gewährleisten.
Der Ablauf dieser Methode
lautet im Groben:
Das Suchpattern wird nach
einem bestimmten Muster aufgebaut. Nähere Informationen dazu gibt es in der Java-API im Paket: java.util.regex.Pattern.
In ConGen wird an dieser Stelle ein Suchpattern
erzeugt und verwendet, das sich wie folgt aufbaut: an erster Stelle steht
(betrachtet wird der Text innerhalb der Hochkommata) die Zeichenkette '.*'. Dadurch
werden beliebige Folgen von Zeichen zugelassen. Danach folgt das erste Zeichen
der Abkürzung des Konferenznamens, gefolgt von der Zeichenkette '[a-z].*'.
Damit wird sichergestellt, dass mindestens ein Kleinbuchstabe auf den
entsprechenden Großbuchstaben folgt. Danach sind weitere Zeichen möglich. Im
weiteren Verlauf wird dieses Vorgehen für jedes Zeichen der Abkürzung
wiederholt. Am Ende wird beispielsweise das Suchpattern für die Abkürzung 'ABC'
so aussehen: '.*A[a-z].*B[a-z].*C[a-z].*'.
Im zweiten Schritt werden
mögliche Sätze aus der NodeList extrahiert. Dafür wird zuerst
der Text jedes Nodes dieser Liste mittels der Methode separateToSentences(String) aus der Klasse StringUtilities in Sätze zerlegt. Ebenfalls in dieser
Klasse existiert die Methode findSentenceByPattern(String, String). Sie sucht aus allen Sätzen
diejenigen heraus, die dem Suchpattern entsprechen. Dies passiert mit den
vordefinierten Methoden des bereits angesprochenen java.util.regex.Pattern-Paketes.
Der dritte Schritt besteht
in der Extraktion des eigentlichen Namens der Konferenz. Wieder wird sich einer
Methode der Klasse StringUtilities bedient. getMatch(String,
String) liefert aus einem Text den
minimalen Teilstring zurück, der dem Suchpattern entspricht. Dieser String ist
der gesuchte Name der Konferenz und wird an die aufrufende getContents()-Methode zurückgegeben, wo
er im entsprechenden Conf –Objekt abgespeichert
wird.
Hinweise:
5.2.6
Methode findLocations(NodeList)
Wie bereits durch den
Methodennamen zu vermuten ist, dient diese Methode der Extraktion des Ortes, an
welchem die Konferenz stattfindet. Dafür erhält die Methode wieder die
entsprechende NodeList als Parameter übergeben
und gibt an die aufrufende getContents()-Methode ein Stringarray
zurück. Dieses wird dort ausgelesen und die interessanten Werte im
entsprechenden Conf -Objekt abgespeichert. An der nullten
Stelle im Array ist die Stadt, in welcher die Konferenz stattfindet
gespeichert. An erster Stelle der Staat und an zweiter Stelle das Land.
Informationen über den Staat werden zwar im weiteren Verlauf nicht benötigt,
aber vielleicht ist die Information im Sinne einer späteren Erweiterung
sinnvoll. In der Beispiel-HTML-Seite wurden zwei mögliche
Standorte des Austragungsortes der Konferenz identifiziert. Nach einer
Separation des Textes der NodeList in einzelne Sätze wird der
entsprechende Ort lokalisiert. In diesem Beispiel kann der Austragungsort am
Ende oder in der Mitte eines Satzes stehen. Je nach Standort wird die weitere
Bearbeitung eingeleitet. Diese Unterscheidung wurde angestrebt, um mögliche
Satzzeichen in die Erkennung mit einzubeziehen.
Die eigentliche Erkennung
erfolgt wieder über Suchpattern. Dafür wird vorher der entsprechende Satz
mittels der Methode separate(String, boolean, boolean)
in eine
Liste von Worten zerlegt. Wichtig in diesem Zusammenhang ist die Methode trimm() der Supportklasse StringUtilities. Sie löscht Satzzeichen
am Ende von Wörtern und entfernt Klammerungen.
5.2.7
Methode findDescription(NodeList)
Diese Methode ist relativ
einfach gehalten, da im vorliegenden Fall die Beschreibung in einem bestimmten
Knoten der NodeList gespeichert ist. Die
einzig verbliebene Aufgabe stellt die Bereinigung des Textes von unerwünschten
Eigenschaften dar. Dafür werden die folgenden Methoden der Klasse StringUtilities eingesetzt.
5.2.8
Methode findLink(NodeList, int)
findLink(NodeList,
int) extrahiert den Link, unter dem weitere
Informationen zur Konferenz zu finden sind. Für die Bearbeitung dieser Aufgabe
werden der Methode wieder die entsprechende NodeList und hier zusätzlich noch
die Position dieser NodeList in der globalen LinkedList entrys
übergeben.
Der Grund dafür ist die Bereinigung dieser Liste
von nun überflüssig
gewordenen Textbestandteilen.
Für die Extraktion des
Links wird in allen Nodes der NodeList nach dem String: "<A HREF=" gesucht. Auf
diese Art wird in HTML-Seiten die Angabe eines Links eingeleitet.
Die einzig dann noch
verbliebene Aufgabe ist die Extraktion des interessierenden Substrings und
seine Rückgabe zur aufrufenden getContents()-Methode, wo die
Information im entsprechenden Conf -Objekt abgespeichert
wird.
5.2.9
Methode findDate(NodeList)
Zum Herausfiltern der
Start- und Endtermine der Konferenz wurde die Methode findDate(NodeList)
geschaffen.
Als Parameter erhält sie die entsprechende NodeList. Zurückgegeben wird ein GregorianCalender-Array. Dieses enthält an der nullten Stelle das Startdatum und an erster Stelle das
Enddatum der Konferenz. Nach der Initialisierung bestimmter Variablen wird im
Beispielparser die Extraktion der gewünschten Informationen eingeleitet. Dafür
wird zuerst der Text in Sätze zerlegt. Dann wird mittels eines Suchpatterns der
Satz herausgefiltert, der eine vierstellige Zahl enthält. Dies ist das Jahr, in
welchem die Konferenz stattfindet. Mit Hilfe der Methode getTwoNumbers(LinkedList)
aus der
Klasse DateUtilities werden aus einer Liste von Wörtern alle Zahlen extrahiert,
die nicht aus vier Ziffern bestehen. Dabei werden auch Zahlen in Verbindung mit
Buchstaben extrahiert (Bsp.: '12' aus '12th'). Das erste Element dieser Liste
ist der Tag des Startdatums und das letzte Element ist der Tag des Enddatums
der Konferenz.
In einem zweiten Schritt
erfolgt die Extraktion der zugehörigen Monate. Dafür wird der Satz, welcher das
Datum enthält in eine Wortliste zerlegt. Aus dieser Liste wird mit der Methode findMonth(LinkedList)
aus der
Klasse DateUtilities eine Liste der Monate ermittelt. Diese Methode ist
nur einsetzbar, wenn deutsche oder englische Monatsnamen verwendet werden. Für
weitere Sprachen ist diese Methode zu erweitern. Für eine andere Angabe der
Monatsnamen sind eigene Methoden zu implementieren. Das erste Element dieser
ermittelten Liste stellt den Monat des Startdatums dar und das letzte Element den
Monat des Enddatums der Konferenz.
Im dritten und letzten
Schritt wird das Jahr ermittelt. Dies erledigt die DateUtilities-Methode findYear(LinkedList). Sie sucht aus einer
Wortliste das Jahr heraus. Das Jahr muss einem der folgenden Formate für
Suchpattern entsprechen: "20\\d\\d",
"19\\d\\d" oder ".*(\\xFA|\\x27|\\x60)\\d\\d\\D.*".
5.2.10
Methode findCFPDate(NodeList)
Dieser Methode wird
ebenfalls die entsprechende NodeList als Parameter übergeben. Der
Typ des Rückgabewertes an die aufrufende getContents()-Methode ist Gregorian-Calender. Auf der Grundlage dieses
Wertes werden im entsprechenden Conf -Objekt das Datum, bis zu
welchen die Paper eingesandt werden müssen und der boolesche Wert, der angibt,
ob ein solches Datum vorhanden ist, gesetzt. Nachdem alle Texte der NodeList in Sätze zerlegt wurden,
wird mittels der StringUtilities-Methode findSentence(LinkedList,
String) der
Satz herausgesucht, welcher das gesuchte Datum enthält. Dafür wird dieser
Methode das Suchwort "due" übergeben, nach
welchem in den Sätzen gesucht wird. Die Informationsstruktur des Beispiels ist so
organisiert, dass im Satz, welcher das gesuchte Datum enthält, dieses Suchwort
vorhanden ist. Dadurch ist eine einfache Lokalisierung des entsprechenden
Satzes möglich. Konnte kein potentieller Satz identifiziert werden, wird das
Jahr des zurückgegebenen
Datums auf '1900' gesetzt,
so dass in der getContents()-Methode das Setzen des booleschen
Wertes erfolgen kann. Wurden potentielle Sätze gefunden, wird der letzte
dieser Sätze in Worte
zerlegt. Mit Hilfe der Methode findDate(LinkedList)
der
Klasse DateUtilities kann das gesuchte Datum extrahiert werden.
5.2.11
Fazit
Im Verlauf der letzten
Abschnitte wurde die Vorgehensweise der Parsererstellung an einem konkreten
Beispiel beschrieben. Es wurde verdeutlicht, welche Konventionen einzuhalten
sind. Die Implementation der eigentlichen
Extraktionsmethoden bleibt der Kreativität des Anwenders überlassen, wobei
dieser auf vorhandene unterstützende Methoden der lassen Wastebasket, StringUtilities und DateUtilities zurückgreifen kann. Eigene
Erweiterungen in den Support-Klassen sollten gut dokumentiert werden. Dabei ist
darauf zu achten, dass Erweiterungen vorhandener Methoden sehr vorsichtig vorzunehmen
sind, da dadurch evtl. die Ausführung bereits existierender Parser eingeschränkt
werden kann. Es ist weiterhin sinnvoll, nachdem aus einer Node alle Informationen extrahiert
sind, diese aus der NodeList zu löschen, um den
Bearbeitungsaufwand für die folgenden Extraktionsmethoden zu verringern.
Für weitergehende
Informationen existiert der bereits erwähnte Bericht
zum Laborpraktikum und der dokumentierte Quellcode. Weiterhin ist es möglich,
sich mit den Autoren in Verbindung zu setzen.
Quellen: Bericht zum Laborpraktikum „ConGen – Dynamische Content-Erweiterung von Webseiten“ von Nicole Mencke und Steffen Kernchen