Open Source im professionellen Einsatz
Linux-Magazin 01/2005

Ein Perl-Skript sammelt automatisch Schlagzeilen

Rasender Reporter

Statt in regelmäßigen Abständen alle interessanten Nachrichtenseiten selbst nach neuen Meldungen abzuklappern, setzt der Perl-Fan lieber einen News-Aggregator darauf an. Der macht seinen Herrn automatisch auf Neuigkeiten aufmerksam. Falls eine Website noch kein RSS-Feed hat, bastelt man es sich selber.

798

Die Informationsflut im Internet ist erdrückend: Wer täglich zwei Dutzend Websites nach neuen Nachrichten absucht, kann gleich seinen Job aufgeben. Nachrichten-Sites fassen darum zunehmend ihre Schlagzeilen mit Links zu den eigentlichen Artikeln in so genannten RSS-Feeds zusammen und stellen sie in maschinenlesbarem Format zur Verfügung. RSS steht für RDF Site Summary, wobei RDF seinerseits das Kürzel für Resource Description Framework ist. RSS-Dateien enthalten XML, das News-Aggregatoren maschinell einlesen. Die noch ungelesenen neuesten Neuigkeiten, präsentieren sie dem Benutzer als klickbare Schlagzeilen.

Diese Syndication, das Zusammenstellen von Nachrichten, die bereits woanders verfügbar sind, hilft dabei, der Informationsflut Herr zu werden, und spart enorm Zeit. Bekannte Sites wie Slashdot und neuerdings sogar die Bild-Zeitung[2] bieten RSS-Feeds an, die Aggregatoren wie Amphetadesk ([3] und Abbildung 1) in regelmäßigen Zeitabständen einsaugen, falls der Benutzer den Service abonniert, also den Subscribe-Knopf drückt. Weitere Informationen rund um RSS-Reader (in der Skriptsprache Tcl) bietet der Artikel "Appetithäppchen" in dieser Ausgabe.

News ohne Feed

Doch nicht jede News-Seite hat ein RSS-Feed. Erwarten deren Anbieter wirklich, dass Nutzer täglich vorbeischneien, um sich durch die angebotenen Informationen zu wühlen? Das heute vorgestellte Modul »RssMaker« stellt eine Funktion bereit, die mit rund zehn Zeilen Perl-Code aus einer Titelseite mit Schlagzeilen und URLs eine RSS-Datei generiert. Per Cronjob einmal täglich neu erzeugt, kann man diese Datei einem News-Aggregator übergeben, der dann eine Zusammenfassung liefert.

Die »make()«-Funktion aus Listing 1 »RssMaker.pm« (Zeile 20) nimmt eine URL entgegen, unter der die News-Site verfügbar ist, lädt die Webseite herunter und wühlt sich durch die darin enthaltenen HTML-Links. Diese Links bietet sie mit dem dargestellten Text einem vom Nutzer definierten Filter an. Der Filter entscheidet, ob die Schlagzeile samt Link in die RSS-Beschreibung aufgenommen wird oder nicht.

Listing 1:
»RssMaker.pm«

001 ###########################################
002 package RssMaker;
003 ###########################################
004 # Mike Schilli, 2004 (m@perlmeister.com)
005 ###########################################
006 use warnings;
007 use strict;
008 
009 use LWP::UserAgent;
010 use HTTP::Request::Common;
011 use XML::RSS;
012 use HTML::Entities qw(decode_entities);
013 use URI::URL;
014 use HTTP::Date;
015 use DateTime;
016 use HTML::TreeBuilder;
017 use Log::Log4perl qw(:easy);
018 
019 ###########################################
020 sub make {
021 ###########################################
022   my(%o) = @_;
023 
024   $o{url}      ||  LOGDIE "url missing";
025   $o{title}    ||  LOGDIE "title missing";
026   $o{output}   ||= "out.rdf";
027   $o{filter}   ||= sub { 1 };
028   $o{encoding} ||= 'utf-8';
029 
030   my $ua = LWP::UserAgent->new();
031 
032   INFO "Fetching $o{url}";
033   my $resp = $ua->request(GET $o{url});
034 
035   LOGDIE "Fetching $o{url} failed" if
036     $resp->is_error();
037 
038   my $http_time =
039             $resp->header('last-modified');
040   INFO "Last modified: $http_time";
041 
042   my $mtime   = str2time($http_time);
043   my $isotime = DateTime->from_epoch(
044                           epoch => $mtime);
045   DEBUG "Last modified: $isotime";
046 
047   my $rss = XML::RSS->new(
048     encoding => $o{encoding});
049 
050   $rss->channel(
051     title => $o{title},
052     link  => $o{url},
053     dc    => { date => $isotime . "Z"},
054   );
055 
056   foreach(exlinks($resp->content(),
057                   $o{url})) {
058 
059     my($lurl, $text) = @$_;
060 
061     $text = decode_entities($text);
062 
063     if($o{filter}->($lurl, $text)) {
064       INFO "Adding rss entry: $text $lurl";
065       $rss->add_item(
066         title => $text,
067         link  => $lurl,
068       );
069     }
070   }
071 
072   INFO "Saving output in $o{output}";
073   $rss->save($o{output}) or
074       die "Cannot write to $o{output}";
075 }
076 
077 ###########################################
078 sub exlinks {
079 ###########################################
080   my($html, $base_url) = @_;
081 
082   my @links = ();
083 
084   my $tree = HTML::TreeBuilder->new();
085 
086   $tree->parse($html) or return ();
087 
088   for(@{$tree->extract_links('a')}) {
089       my($link, $element, $attr,
090          $tag) = @$_;
091 
092       next unless $attr eq "href";
093 
094       my $uri = URI->new_abs($link,
095                              $base_url);
096       next unless
097         length $element->as_trimmed_text();
098 
099       push @links,
100            [URI->new_abs($link, $base_url),
101             $element->as_trimmed_text()];
102     }
103 
104     return @links;
105 }
106 
107 1;

Falls ja, fügt das Skript neben der Schlagzeile und dem Link auch noch das letzte Modifikationsdatum der Webseite in das Newsfeed ein - ein kleiner Trick, um Nachrichten mit einem Datum zu versehen, auch wenn diese selbst kein Datum führen. Am Ende schreibt »make« die XML-Ausgabe in eine mit dem Parameter »output« spezifizierte Datei, die ein News-Aggregator später aufgreifen kann.

Datum in zwei Formaten

Um den HTTP-Zeitstempel des Webdokuments in das vom RSS-Format geforderte ISO-8601-Format umzuwandeln, scannt die Funktion »str2time« aus dem Modul »HTTP::Date« zunächst das Stringformat (zum Beispiel »Tue, 26 Oct 2004 05:10:08 GMT«) ein und gibt die Unix-Zeit in Sekunden zurück. Den Wert schnappt sich die »from_epoch()«-Funktion aus dem »DateTime«-Modul und erzeugt ein neues »DateTime«-Objekt, das sich im Stringkontext magisch in das ISO-8601-Format verwandelt: »2004-10-26T05:10:08«.

XML erwartet UTF-8-kodierten Text. UTF-8 ist kompatibel mit regulärem Ascii - solange keine Zeichen aus der zweiten Hälfte der 256-Zeichen-Tabelle enthalten sind. Die deutschen Umlaute sind also wieder mal ein Problem: Werden nach ISO-8859-1 alle 256 Zeichen der Ascii-Tabelle mit den Umlauten genutzt, ist die zweite Hälfte (129 bis 256) nicht UTF-8-kompatibel.

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • Dosenfutter

    Über XML-basierte RSS-Feeds geben viele Websites maschinenlesbare Zusammenfassungen ihrer Inhalte. Die meisten Programmiersprachen kommen mit dem Format gut klar.

  • Foto-Labor

    Gimp, das GNU Image Manipulation Program, verbessert und manipuliert digitale Bilder mit anspruchsvollen Filterfunktionen. Statt immer wieder die gleichen Manöver mit der Maus auszuführen, lassen sich die Vorgänge mit Gimps Perl-Schnittstelle sogar automatisieren.

  • Perl-Snapshot Linux-Magazin 2012/12

    Perlmeister Michael Schilli hat seinen Snapshot verfilmt.

  • Perl-Snapshot Linux-Magazin 2011/05

    "Perlmeister" Michael Schilli hat seinen Snapshot aus dem Magazin 2011/05 als Screencast verarbeitet.

  • Der Screencast zum Perl-Snapshot Linux-Magazin 06/2013

    Statt die Fenster einer Entwicklungsumgebung stets aufs Neue manuell zurechtzuzupfen, lässt Perl-Meister Michael Schilli den Terminal-Multiplexer Tmux nur seine Konfigurationsdateien restaurieren.

comments powered by Disqus

Ausgabe 07/2017

Digitale Ausgabe: Preis € 6,40
(inkl. 19% MwSt.)

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.