Farid Hajji: Perl - Einführung, Anwendungen, Referenz
2., aktualisierte und erweiterte Auflage
Addison-Wesley Longman, ISBN 3-8273-1535-2
Das Parsen von HTML-Texten ist nicht ganz einfach. Mit der Verwendung einfacher regulärer Ausdrücke ist es noch nicht getan: Kommentare können beispielsweise ebenfalls HTML-Tags enthalten, wodurch einfache Mechanismen versagen können.
Glücklicherweise gibt es das Modul HTML::Parser aus dem CPAN. Dieses Modul kann nicht nur HTML-Daten, sondern ganz allgemein SGML-Daten parsen.
HTML::Parser-ModulWie wird nun HTML::Parser verwendet? Da dieses Modul als Basisklasse entworfen wurde, sollten Sie eine Subklasse von HTML::Parser, wie in Kapitel 15 gezeigt, bilden. Anschließend können Sie einzelne Funktionen von HTML::Parser überladen. Diese Funktionen sind Callbacks, die automatisch vom Parser aufgerufen werden, wenn ein Tag, normaler Text etc. erkannt werden.
Die Manual-Seite man HTML::Parser liefert eine komplette Liste der überladbaren Funktionen. Wird eine Funktion nicht überladen, so tut sie in den meisten Fällen gar nichts.
Einige nützliche Funktionen sind:
$self->start($tag, $attr, $attrseq, $origtext)</Tag> vorliegen.
$tag enthält den Namen des Tags in Kleinbuchstaben.$attr ist eine Referenz auf einen anonymen Hash, der als Schlüssel die Attribute des Tags in Kleinbuchstaben und als zugehörige Werte die Werte dieser Attribute enthält. Entitäten, wie z.B. kleiner-als und größer-als wurden bereits in ihre richtige Form (< und >) gebracht.$attrseq ist eine Referenz auf einem Array von Attributen, in Kleinbuchstaben aber in der ursprünglichen Reihenfolge. Das ist nützlich, da im Hash %$attr die Reihenfolge der Schlüssel bekanntlich durcheinandergebracht wird.$origtext enthält den ursprünglichen HTML-Text des gesamten Tags, von der &oouml;ffnenden bis zur schliessenden Spitzenklammer.$self->end($tag, $origtext)$tag ist der Name des Tags in Kleinbuchstaben.$origtext enthält den ursprünglichen HTML-Text des Ende-Tags.$self->text($text)decode() des Moduls HTML::Entities.
$text enthält den Text-Chunk, der gerade erkannt wurde.$self->comment($comment)-- Sequenzen wurden bereits aus dem Kommentartext entfernt.
$comment enthält den Kommentartext, ohne die oben erwähnten -- Sequenzen.Das Programm html-parse.pl bildet eine Unterklasse von HTML::Parser mit Hilfe des Schlüsselworts package und des Arrays @ISA. Anschließend werden die Methoden start(), end() und text() überladen.
Beachten Sie die Signaturen der Funktionen! Da es sich um Methoden handelt, wird implizit als erstes Argument immer $self mit übergeben. Erst dann folgt die normale Signatur:
# Die start()-Methode wird bei jedem Start-Tag aufgerufen:
sub start {
my ($self, $tag, $attr, $attrseq, $origtext) = @_;
In den überladenen Funktionen geben wir einfach die Werte der Argumente aus. HTML-Text wird dabei nicht ausgegeben! Wenn Sie dies möchten, sollten Sie den $origtext der gewünschten Funktionen ausgeben. In unserem Fall beschränken wir uns auf die Ausgabe der Argumente.
Die Argumente $attr und $attrseq sind Zeiger auf Hashes bzw. auf Arrays. Um diese bequem auszugeben, benutzen wir die Funktion Dumper des Stringifizierungsmoduls Data::Dumper, wie dies bereits in Kapitel 13 und Kapitel 18 gezeigt wurde. Dies hätten wir auch genauso gut wie folgt schreiben:
# $attr ist ein Zeiger auf einen Hash.
print "{",
join(",",
map { "$_ => $attr->{$_}" } sort keys %{ $attr }
),
"}\n";
# $attrseq ist ein Zeiger auf ein Array.
print "[", join(",", @{ $attrseq }), "]\n";
Die einzelnen Text-Chunks werden in der Methode text() ausgeben. Damit auch Newlines und Whitespaces sichtbar werden, wurden die Text-Chunks in eckigen Klammern ausgegeben.
Bei folgender Eingabedatei:
<html>
<!-- Eine Testseite für html-parse.pl -->
<head>
<title>Das ist ein Test-Titel</title>
<link rel=stylesheet type="text/css" href="../perlv2.css">
<link rev="Prev" href="../ew/html-parse-intro.html">
<meta name="Author" content="Farid Hajji">
<meta name="keywords" content="HTML::Parser,testseite">
</head>
<body>
<div class="navbar">
<a href="../cgiwww.html"><img src="../icons/left.gif" width=29
height=30 border=0 align=bottom alt="[Kapitel 19]"></a>
<a href="../index.html"><img src="../icons/up.gif" width=29
height=30 border=0 align=bottom alt="[Hauptseite]"></a>
</div>
<div class="title" align="center">
Das ist der Titel
<br>
Hier ist der Untertitel
</div>
<h1>Eine erste Überschrift</h1>
Text der ersten Überschrift. Dieser Text erstreckt sich
über mehrere Zeilen in der HTML-Datei.</body></html>
sieht die Ausgabe des Programms wie folgt aus:
farid@sun-1:~/ex> ./html-parse.pl html-parse.test
Recognized Tag: html
Attributes:
$VAR1 = {};
Sequence of Attributes:
$VAR1 = [];
Original Tag was: <html>
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized Tag: head
Attributes:
$VAR1 = {};
Sequence of Attributes:
$VAR1 = [];
Original Tag was: <head>
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized Tag: title
Attributes:
$VAR1 = {};
Sequence of Attributes:
$VAR1 = [];
Original Tag was: <title>
------------------------------------------------------------
Recognized Text-Chunk:
[Das ist ein Test-Titel]
------------------------------------------------------------
Recognized End-Tag: title
Orignal End-Tag was: </title>
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized Tag: link
Attributes:
$VAR1 = {
'href' => '../perlv2.css',
'type' => 'text/css',
'rel' => 'stylesheet'
};
Sequence of Attributes:
$VAR1 = [
'rel',
'type',
'href'
];
Original Tag was: <link rel=stylesheet type="text/css" href="../perlv2.css">
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized Tag: link
Attributes:
$VAR1 = {
'rev' => 'Prev',
'href' => '../ew/html-parse-intro.html'
};
Sequence of Attributes:
$VAR1 = [
'rev',
'href'
];
Original Tag was: <link rev="Prev" href="../ew/html-parse-intro.html">
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized Tag: meta
Attributes:
$VAR1 = {
'content' => 'Farid Hajji',
'name' => 'Author'
};
Sequence of Attributes:
$VAR1 = [
'name',
'content'
];
Original Tag was: <meta name="Author" content="Farid Hajji">
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized Tag: meta
Attributes:
$VAR1 = {
'content' => 'HTML::Parser,testseite',
'name' => 'keywords'
};
Sequence of Attributes:
$VAR1 = [
'name',
'content'
];
Original Tag was: <meta name="keywords" content="HTML::Parser,testseite">
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized End-Tag: head
Orignal End-Tag was: </head>
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized Tag: body
Attributes:
$VAR1 = {};
Sequence of Attributes:
$VAR1 = [];
Original Tag was: <body>
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized Tag: div
Attributes:
$VAR1 = {
'class' => 'navbar'
};
Sequence of Attributes:
$VAR1 = [
'class'
];
Original Tag was: <div class="navbar">
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized Tag: a
Attributes:
$VAR1 = {
'href' => '../cgiwww.html'
};
Sequence of Attributes:
$VAR1 = [
'href'
];
Original Tag was: <a href="../cgiwww.html">
------------------------------------------------------------
Recognized Tag: img
Attributes:
$VAR1 = {
'alt' => '[Kapitel 19]',
'height' => 30,
'border' => '0',
'src' => '../icons/left.gif',
'align' => 'bottom',
'width' => 29
};
Sequence of Attributes:
$VAR1 = [
'src',
'width',
'height',
'border',
'align',
'alt'
];
Original Tag was: <img src="../icons/left.gif" width=29
height=30 border=0 align=bottom alt="[Kapitel 19]">
------------------------------------------------------------
Recognized End-Tag: a
Orignal End-Tag was: </a>
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized Tag: a
Attributes:
$VAR1 = {
'href' => '../index.html'
};
Sequence of Attributes:
$VAR1 = [
'href'
];
Original Tag was: <a href="../index.html">
------------------------------------------------------------
Recognized Tag: img
Attributes:
$VAR1 = {
'alt' => '[Hauptseite]',
'height' => 30,
'border' => '0',
'src' => '../icons/up.gif',
'align' => 'bottom',
'width' => 29
};
Sequence of Attributes:
$VAR1 = [
'src',
'width',
'height',
'border',
'align',
'alt'
];
Original Tag was: <img src="../icons/up.gif" width=29
height=30 border=0 align=bottom alt="[Hauptseite]">
------------------------------------------------------------
Recognized End-Tag: a
Orignal End-Tag was: </a>
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized End-Tag: div
Orignal End-Tag was: </div>
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized Tag: div
Attributes:
$VAR1 = {
'class' => 'title',
'align' => 'center'
};
Sequence of Attributes:
$VAR1 = [
'class',
'align'
];
Original Tag was: <div class="title" align="center">
------------------------------------------------------------
Recognized Text-Chunk:
[
Das ist der Titel
]
------------------------------------------------------------
Recognized Tag: br
Attributes:
$VAR1 = {};
Sequence of Attributes:
$VAR1 = [];
Original Tag was: <br>
------------------------------------------------------------
Recognized Text-Chunk:
[
Hier ist der Untertitel
]
------------------------------------------------------------
Recognized End-Tag: div
Orignal End-Tag was: </div>
------------------------------------------------------------
Recognized Text-Chunk:
[
]
------------------------------------------------------------
Recognized Tag: h1
Attributes:
$VAR1 = {};
Sequence of Attributes:
$VAR1 = [];
Original Tag was: <h1>
------------------------------------------------------------
Recognized Text-Chunk:
[Eine erste Überschrift]
------------------------------------------------------------
Recognized End-Tag: h1
Orignal End-Tag was: </h1>
------------------------------------------------------------
Recognized Text-Chunk:
[
Text der ersten Überschrift. Dieser Text erstreckt sich
über mehrere Zeilen in der HTML-Datei.]
------------------------------------------------------------
Recognized End-Tag: body
Orignal End-Tag was: </body>
------------------------------------------------------------
Recognized End-Tag: html
Orignal End-Tag was: </html>
------------------------------------------------------------
Wir erkennen, daß die Tags richtig erkannt wurden und auch die entsprechenden Methoden getriggert haben. Interessant ist auch, daß hier die Verschachtelung der Tags offenbar nicht vom Parser berücksichtig wurde. Dies sollten Sie natürlich bei Bedarf selbst tun oder weitere fertige HTML-Klassen aus dem CPAN verwenden.
HTML::Parser gibt es noch eine Reihe weiterer nützlicher HTML-Klassen:
HTML::TokenParserget_token() den jeweils nächsten Token zurück. Ein Token ist hierbei eine Referenz auf einem Array:
["S", $tag, %$attr, @$attrseq, $origtext] Start-Tag ["E", $tag, $origtext] Ende-Tag ["T", $text] Normaler Text ["C", $text] Kommentar ["D", $text] Deklaration
HTML::Parser erkannt werden.HTML::TokenParser brauchen Sie keine Subklasse zu definieren. Außerdem sollten Sie nicht Aufrufe der Methoden von HTML::Parser und HTML::TokenParser durcheinanderbringen. Die Manualseite man HTML::TokenParser enthält eine Liste von Funktionen, sowie typische Anwendungsbeispiele.
# This example extract all links from a document. It will
# print one line for each link, containing the URL and the
# textual description between the ... tags:
use HTML::TokeParser;
$p = HTML::TokeParser->new(shift||"index.html");
while (my $token = $p->get_tag("a")) {
my $url = $token->[1]{href} || "-";
my $text = $p->get_trimmed_text("/a");
print "$url\t$text\n";
}
# This example extract the <TITLE> from the document:
use HTML::TokeParser;
$p = HTML::TokeParser->new(shift||"index.html");
if ($p->get_tag("title")) {
my $title = $p->get_trimmed_text;
print "Title: $title\n";
}
HTML::HeadParserHTML::HeadParser ist eine spezialisierte Version von HTML::Parser, die nur die <HEAD> Sektion eines HTML-Dokumentes analysiert. Um beispielsweise den <TITLE> eines Dokumentes zu extrahieren, kann man wie folgt vorgehen:
use HTML::HeadParser;
my $hp = HTML::HeadParser->new(); # Objekt erzeugen
$hp->parse($some_html_text) # HTML-Text parsen
and print "not finished"; # false, falls nichts mehr
# zu parsen ist.
print $hp->header('Title'); # Den <TITLE> extrahieren
header()-Methode gehört in Wirklichkeit zu einer Hilfsklasse: HTTP::Headers. Eine Instanz dieser Klasse wird durch parse() nach und nach mit den Daten des <HEAD> gefüllt. Der Aufruf $hp->header('Title') ist nichts anderes als $hp->header->header('Title'), wobei der erste Aufruf das HTTP::Headers-Hilfsobjekt zurückliefert, das mittels header() die Information wieder zurückgibt. Weitere Informationen finden Sie in den Manual-Seiten man HTML::HeadParser und man HTTP::Headers.
HTML::LinkExtorHTML::LinkExtor, das eine Subklasse von HTML::Parser ist, können Links problemlos aus einem HTML-Dokument extrahiert werden.
html-parse-links1.pl werden sätliche Links eines HTML-Dokuments mittels HTML::LinkExtor und einer Callback-Funktion angezeigt. Auf unserer Testseite angewandt:
<html>
<!-- Eine Testseite für html-parse.pl -->
<head>
<title>Das ist ein Test-Titel</title>
<link rel=stylesheet type="text/css" href="../perlv2.css">
<link rev="Prev" href="../ew/html-parse-intro.html">
<meta name="Author" content="Farid Hajji">
<meta name="keywords" content="HTML::Parser,testseite">
</head>
<body>
<div class="navbar">
<a href="../cgiwww.html"><img src="../icons/left.gif" width=29
height=30 border=0 align=bottom alt="[Kapitel 19]"></a>
<a href="../index.html"><img src="../icons/up.gif" width=29
height=30 border=0 align=bottom alt="[Hauptseite]"></a>
</div>
<div class="title" align="center">
Das ist der Titel
<br>
Hier ist der Untertitel
</div>
<h1>Eine erste Überschrift</h1>
Text der ersten Überschrift. Dieser Text erstreckt sich
über mehrere Zeilen in der HTML-Datei.</body></html>
html-parse-link1.pl folgende Ausgabe:
farid@sun-1:~/ex> ./html-parse-links1.pl html-parse.test <a>-Links : ../cgiwww.html ../index.html <img>-Links: ../icons/left.gif ../icons/up.gif Other Links: ../perlv2.css ../ew/html-parse-intro.html
LWP-Library, wissen wir bereits die absolute URL.<HEAD> ein Tag <base href="http://..."> vorhanden ist, den man z.B. mit HTML::HeadParser wie folgt extrahieren kann:
use HTML::HeadParser;
$p = HTML::HeadParser->new;
$p->parse($text) and print "not finished";
$base = $p->header('Content-Base');
HTML::LinkExtor-Objektes als zweites Argument angeben. Dies ist in html-parse-links2.pl geschehen. Das sonstige Programm ist unverändert geblieben.farid@sun-1:~/ex> ./html-parse-links2.pl html-parse.test \ > http://localhost/~farid/bv2/ew/ <a>-Links : http://localhost/~farid/bv2/cgiwww.html http://localhost/~farid/bv2/index.html <img>-Links: http://localhost/~farid/bv2/icons/left.gif http://localhost/~farid/bv2/icons/up.gif Other Links: http://localhost/~farid/bv2/perlv2.css http://localhost/~farid/bv2/ew/html-parse-intro.html
..-Komponenten in den URLs richtig aufgelöst wurden!
html-parse-links3.pl nun automatisch gemacht. Dieses, aus man HTML::LinkExtor inspirierte Beispiel, geht davon aus, daß wir das HTML-Dokument selbst mit Hilfe der LWP-Library downloaden. Somit steht einer Konvertierung der Links mit Hilfe einer bekannten Basis nichts mehr im Wege.HTML::LinkExtor übergeben. Diese ist ja noch nicht im voraus bekannt. Vielmehr wird die Basis aus dem HTTP::Request-Objekt extrahiert und mit Hilfe des URI::URL-Moduls an die einzelnen Links angehängt.html-parse-links3.pl, angewandt auf unsere Test-Seite ergibt nun:
farid@sun-1:~/ex> ./html-parse-links3.pl \ > http://localhost/~farid/perlv2/ex/html-parse.test <a>-Links : http://localhost/~farid/perlv2/cgiwww.html http://localhost/~farid/perlv2/index.html <img>-Links: http://localhost/~farid/perlv2/icons/left.gif http://localhost/~farid/perlv2/icons/up.gif Other Links: http://localhost/~farid/perlv2/perlv2.css http://localhost/~farid/perlv2/ew/html-parse-intro.html
[Prev] [Up] [Next]
[Alte Quelle]
| Last modified: $Date: 2006/05/18 12:55:48 $ FH. Search :: Sitemap :: Disclaimer :: Copyright :: Privacy |
|