Neben Google, Amazon und Ebay bietet neuerdings auch Yahoo eine Programmierschnittstelle zu einigen seiner Dienste an. Drei einfache Perl-Skripte stöbern heute damit Tippfehler, Internetbildchen und verschollen geglaubte Klassenkameraden auf.
Immer mehr Internetanbieter stellen ihre Dienste nicht nur über eine Browserschnittstelle ins Netz, sondern erlauben es Entwicklern, sie in ihre eigenen Applikationen mittels Web-APIs einzuklinken. Yahoo bietet seit neuestem ein REST-Interface (Representational State Transfer, in der Praxis: URLs hin, XML zurück) zu seiner Such-Engine an. Dazu gibt es natürlich auch das passende Perl-Modul Yahoo::Search, erhältlich im CPAN und entwickelt von einem Kollegen bei Yahoo, dem großen Regex-Spezialisten Jeffrey Friedl.
Es vereinfacht die Suche nach Dokumenten, Bildern, Videos und einiges mehr. HTTP-Zugriffe und XML-Extraktion sind sauber hinter einfachen Methodenaufrufen versteckt. Wer eine Applikation entwickeln möchte, holt sich einfach eine eigene Application-ID ab. Sie berechtigt zu 5000 Aufrufen der in diesem Artikel vorgestellten Dienste pro Tag. Die Registrierung auf[2] erfordert eine eigene Yahoo-ID, die es ebenfalls kostenlos gegen die Angabe einer gültigen E-Mail-Adresse gibt. Es empfiehlt sich allerdings auch ein Blick in das Privacy Statement, das unter anderem beschreibt, wofür Yahoo die Mailadresse verwenden möchte.
Meinten Sie …?
Wer nicht genau weiß, wie ein bestimmtes Wort geschrieben wird, greift zum Duden oder einem Englisch-Wörterbuch. Allerdings finden sich darin manche aktuellen oder gerade modischen Wörter, Begriffe der Popkultur oder Eigennamen nicht. In solchen Fällen hilft das Internet weiter. Die meisten Such-Engines bieten “Did you mean?”-Funktionen an. Zu Suchwörtern, die ihnen unverständlich erscheinen, schlagen sie korrigierte Schreibweisen vor.
Das Programm »typo« beispielsweise (siehe Listing 1 ) ruft über die Methode »Term()« den Korrektur-Webservice bei Yahoo auf:
$ typo foo foughters Corrected: foo fighters
Wer also ein Lied im Radio hört, aber den Bandnamen nicht richtig versteht, gibt einfach das Gehörte in die Suchmaschine ein. Falls die Gruppe recht bekannt ist, kann es die Korrektur möglicherweise richten.
Listing 1 zeigt die Implementierung: Die »use«-Anweisung in Zeile 9 holt das zuvor mit einer CPAN-Shell installierte Modul Yahoo::Search rein und übergibt ihm die vorher von[2] abgeholte Application-ID. An dieser Stelle sind dieses Skript und die beiden folgenden vor dem Einsatz entsprechend anzupassen.
Die Methode »Terms()« nimmt mit dem Parameter »Spell« ein Wort oder eine Wortfolge entgegen, schickt sie an den Service auf der Yahoo-Seite, untersucht das zurückgeschickte XML und fieselt eine etwaige Antwort heraus. Zeile 11 legt sie in der Variablen »$suggestion« ab. Das anschließende If-else-Konstrukt gibt entweder die Antwort aus oder, falls der Such-Engine nichts zum untersuchten Begriff eingefallen ist, »No corrections«. Leider hinkt die Internationalisierung des Service noch etwas hinterher und zu Wörtern mit Umlauten fallen ihm noch keine Korrekturen ein.
Auf der Suche nach der verlorenen Zeit
Die Spiders der Search-Engines grasen ständig das bekannte Internet nach neu auftauchenden Informationen ab. Entsprechend ändern sich die Suchresultate zu bestimmten Begriffen. Wer einmal am Tag eine Abfrage mit den Namen seiner alten Schulkameraden abschickt, bekommt mit, falls diese neue Homepages aufziehen oder zu Rang und Namen kommen. Natürlich hat niemand die Zeit, das jeden Tag manuell durchzuführen. Auch könnte man sich schwer die Resultate merken.

Abbildung 1: Alte Klassenkameraden sind im Web aufgetaucht. Das Skript verschickt eine Zusammenfassung der Treffer via E-Mail.
Suchdienst
Das Skript »buddy« (Listing 2) startet deshalb täglich per Cronjob und holt jeweils die ersten 25 Ergebnisse zu einer Liste von Namen ein, die in der Konfigurationsdatei »~/.buddy« liegen. Alle bisher unbekannten URLs und einen kurzen Ausschnitt aus dem zur Suchabfrage passenden Textteil der gefundenen Webseite schickt »buddy« an eine vorgegebene E-Mail-Adresse. So bleibt der Empfänger auf dem Laufenden, er erfährt, wenn einer der gesuchten Namen irgendwo auftaucht – vielleicht sogar bei einer Nobelpreis-Nominierung.
Buddy hält die so gefundenen URLs in einem Cache vorrätig, der sich die Einträge einen Monat lang merkt. Danach behandelt das Programm wiedergefundene Treffer so, als wären sie gerade zum ersten Mal aufgetaucht. So simuliert der Cache ein schlechtes Gedächtnis und man kann sich freuen, falls ein alter Name nach einem Monat Abwesenheit wieder auftaucht.
In Zeile 6 erwartet das Skript die E-Mail-Adresse, an die es die gefundenen Änderungen verschicken soll. Wird »buddy« von der Kommandozeile mit der Option »-v« (für verbose) aufgerufen, dann initialisiert Zeile 18 das Log4perl-Framework mit dem Log-Level »$DEBUG« und macht es damit gesprächiger. Andernfalls würden nur Log-Messages der Priorität »WARN« und höher ausgegeben.
Zeile 20 deklariert die weiter unten definierte Funktion »mailadd«, damit Perl weiß, dass es sich um eine Funktion handelt, und Aufrufe schon vor der Definition ohne lästige Klammern erfolgen können. »mailadd« enthält eine »our«-Variable namens »$maildata«, die es sich mit der ab Zeile 82 definierten Funktion »mailsend« teilt. Aufrufe von »mailadd« hängen nur Text an »$maildata« an. Der so gespeicherte Text wird anschließend beim Aufruf von »mailsend« per Mail:: Send an die angegebene E-Mail-Adresse abgeschickt.
Die aus dem Modul Sysadm::Install importierte Funktion »plough« (pflügen), nimmt in Zeile 24 eine Callback-Funktion und einen Dateinamen entgegen. Sie liest die Datei ein, ruft die Callback-Funktion nach jeder gelesenen Zeile auf und übergibt den Zeileninhalt in der Variablen »$_«. Zeile 25 verwirft alle mit »#« beginnenden Kommentarzeilen und der »chomp«-Befehl sägt den Zeilenumbruch ab. Zeile 27 schiebt gefundene Buddies ans Ende des dadurch stetig wachsenden Array »@buddies«.
Zeile 42 kontaktiert danach über die »Results()«-Methode den Yahoo-Dienst. Der Name eines Klassenkameraden aus der Konfigurationsdatei wird in doppelte Anführungszeichen eingerankt und dann als String »qq{“$buddy”}« mit dem »Doc«-Parameter übergeben, da es sich um eine Websuche handelt.
Die als Liste zurückkommenden Ergebnisobjekte geben über die Methoden »Url()« und »Summary()« die URL und die Kurzbeschreibungen der ermittelten Treffer aus. Der File-Cache (Zeile 30) wird hinter den Kulissen in »/tmp/FileCache« angelegt, er speichert die Einträge wegen des auf 30 Tage gesetzten Parameters »default_expires_in« einen Monat lang.
Da sich der Webservice strikt an UTF-8 hält, müssen die Namen UTF-8-kodiert in »~/.buddy« stehen. Wer eine neuere Linux-Distribution fährt, dessen Editor wird sie gleich in UTF-8 abspeichern. Wer mit Latin 1 arbeitet, kann die Daten mit einem kleinen Programm wie »toutf8« und einem Kommandoaufruf wie »toutf8 buddy.latin1 >~/.buddy« schnell konvertieren:
# toutf8
use Text::Iconv;
my $conv = Text::Iconv->new("Latin1",
"UTF-8");
print $conv->convert(join '', <>);
Im Skript ist lediglich die E-Mail-Adresse in Zeile 6 an die lokalen Gegebenheiten anzupassen. Ein Cron-Eintrag der Form »0 5 * * * $HOME/bin/buddy« ruft das Skript dann jeden Morgen auf. Es klappert die Such-Engine ab, frischt den Cache auf und schickt eine E-Mail, falls sich seit der letzten Suche etwas getan hat.

Abbildung 2: Die Bildersuche aus Listing 3 liefert eine spannende Diashow, hauptsächlich mit privaten Urlaubsfotos.
|
Listing 1: |
|---|
01 #!/usr/bin/perl -w
02 use strict;
03
04 my $term = "@ARGV";
05
06 die "usage: $0 word/phrase ..."
07 unless length $term;
08
09 use Yahoo::Search AppId => "eigene_ID";
10
11 my($suggestion) = Yahoo::Search->Terms(
12 Spell => $term);
13
14 if(defined $suggestion) {
15 print "Corrected: $suggestionn";
16 } else {
17 print "No suggestionsn";
18 }
|
Bilderreigen
Auch die Suche nach Bildern unterstützt der neue Service. Zu einem Begriff findet die Search-Engine eine Reihe passender Bilder, die das Skript »slideshow« (siehe Listing 3) dann im 5-Sekunden-Takt wie in einer Diashow nach und nach im Browser darstellt.
Wie Abbildung 3 zeigt, stellt das Skript zunächst ein einfaches Suchformular dar. Trägt der Benutzer dort einen Suchbegriff ein (zum Beispiel »San Francisco«) und klickt auf den »Search«-Knopf, wird der CGI-Parameter »q« gesetzt und Zeile 36 ruft die Methode »Results()« des Pakets Yahoo::Search auf. Der Parameter »Image« übergibt den Suchbegriff, ein »Count« limitiert das Ergebnis auf 50 Treffer und ein auf »0« gesetztes »AllowAdult« verhindert mehr oder weniger erfolgreich, dass sich plötzlich Nackte eindeutig auf dem Bildschirm tummeln.
Da der Text der Bildunterschrift in UTF-8 daherkommt, zeigt die Methode »header()« in Zeile 18 dem Browser neben den üblichen Header-Zeilen an, dass die dynamisch erzeugte Webseite in UTF-8 kodiert ist.

Abbildung 3: Eine Anfrage nach »San Francisco« in der Eingabemaske der Bildersuche bereitet eine Slideshow mit Stadtansichten vor.
|
Listing 2: |
|---|
01 #!/usr/bin/perl -w
02
03 use strict;
04
05 my $BUDDY_FILE = "$ENV{HOME}/.buddy";
06 my $EMAIL_TO = 'meldung@ich.lausche.com';
07
08 use Sysadm::Install qw(:all);
09 use Yahoo::Search;
10 use Text::Wrap;
11 use Cache::FileCache;
12 use Log::Log4perl qw(:easy);
13 use Getopt::Std;
14 use Mail::Send;
15
16 getopts("v", my %o);
17
18 Log::Log4perl->easy_init($o{v} ?
19 $DEBUG : $WARN);
20 sub mailadd;
21
22 my @buddies = ();
23
24 plough sub {
25 return if /^s*#/;
26 chomp;
27 push @buddies, $_;
28 }, $BUDDY_FILE;
29
30 my $cache = Cache::FileCache->new({
31 namespace => "Buddy",
32 default_expires_in => 3600*24*30,
33 });
34
35 my $search = Yahoo::Search->new(
36 AppId => "eigene_ID",
37 Count => 25,
38 );
39
40 for my $buddy (@buddies) {
41 DEBUG "Search request for '$buddy'";
42 my @results = $search->Results(
43 Doc => qq{"$buddy"}
44 );
45
46 my $buddy_printed = 0;
47
48 DEBUG scalar @results, " results";
49
50 for my $result (@results) {
51
52 if($cache->get($result->Url())) {
53 DEBUG "Found in cache: ",
54 $result->Url();
55 # Refresh if found
56 $cache->set($result->Url(), 1);
57 next;
58 }
59
60 mailadd "nn### $buddy ###"
61 unless $buddy_printed++;
62
63 mailadd $result->Url();
64
65 $cache->set($result->Url(), 1);
66
67 mailadd fill(" ", " ",
68 $result->Summary()), "";
69 }
70 }
71
72 mailsend();
73
74 ###########################################
75 sub mailadd {
76 ###########################################
77 our $maildata;
78 $maildata .= "$_n" for @_;
79 }
80
81 ###########################################
82 sub mailsend {
83 ###########################################
84 our $maildata;
85
86 return unless defined $maildata;
87
88 DEBUG "Sending email: $maildata";
89
90 my $msg = Mail::Send->new();
91 $msg->to($EMAIL_TO);
92 $msg->subject("Buddy Watch News");
93 my $fh = $msg->open;
94 print $fh $maildata;
95 close $fh;
96 }
|
Nacheinander in den Cache
Das Skript »slideshow« speichert das Ergebnis einer Suchabfrage, also die Image-URLs und deren Summary-Texte, als Array von Arrays in einem persistenten Datei-Cache ab. Auf diese Weise muss der Diaprojektor nicht bei jedem neuen Bild erst wieder zur Such-Engine zurückkehren.
Das Modul »Cache::FileCache« legt Key-Value-Paare ab, wobei aber als Werte nur einfache Skalare und keine verschachtelten Strukturen erlaubt sind. Das Modul »Storable« hilft aus der Patsche, denn dessen Funktion »freeze()« kann eine Datenstruktur serialisieren, bevor sie in den Cache wandert. Kommt der serialisierte Datensalat wieder aus dem Cache zurück, wandelt ihn der De-Serialisierer »thaw()« in die ursprüngliche verschachtelte Perl-Datenstruktur um.
Damit das CGI-Skript hereinkommende Daten nicht versehentlich ungeprüft für Systemaufrufe verwendet und damit Sicherheitslöcher in die Applikation reißt, steht ganz am Anfang in der Shebang-Zeile hinter dem Aufruf des Perl-Interpreters die Option »-T« , die den Taint Mode aktiviert.
Der erste »if«-Block ab Zeile 20 kommt zum Einsatz, falls das Skript sowohl mit dem Query-String also auch mit der fortlaufenden Nummer des aktuellen Bildes aufgerufen wurde. Ist dies der Fall, steht im Cache die Folge der Bild-URLs zusammen mit den zugehörigen Texten. Zeile 21 taut den Array von Arrays auf und der Modulo-Operator in Zeile 23 sorgt dafür, dass die fortlaufend hochgezählte Bildnummer immer in den Array hinein zeigt und nie über dessen Ende hinausschießt.
Die in Zeile 24 mit dem Parameter »5« aufgerufene Funktion »refresh()« ist ab Zeile 64 definiert. Sie gibt HTML-Sequenzen zurück, die den Browser durch Meta-Tags dazu veranlassen, nach dem Ablauf der übergebenen Anzahl von Sekunden die nächste Seite mit dem nächsten Bild zu laden.
Ein weiterer Parameter, »$reset« in Zeile 73, legt dann noch fest, ob die nachzuladende URL das nächste Bild anzeigt (»next_url« zählt einfach den Nummern-Parameter »s« um eins hoch) oder ob es mit der Ursprungs-URL zurück zur Startseite geht.
Wer das Skript installieren will, verfrachtet es einfach ins Verzeichnis »cgi-bin« des Webservers. (jcb)
|
Listing 3: |
|---|
01 #!/usr/bin/perl -wT
02
03 use strict;
04
05 use CGI qw(:all);
06 use Yahoo::Search AppId => "eigene_ID";
07 use Cache::FileCache;
08 use Storable qw(freeze thaw);
09
10 my $cache = Cache::FileCache->new({
11 namespace => 'slideshow',
12 default_expires_in => 3600,
13 auto_purge_on_set => 1,
14 });
15
16 my $data;
17
18 print header(-charset => "utf-8");
19
20 if(param('q') and defined param('s')) {
21 $data = thaw $cache->get(param('q'));
22 my $seq = param('s');
23 $seq %= scalar @$data;
24 print refresh(5);
25 print center(
26 a({href => url()}, "Stop"),
27 a({href => next_url()}, "Next"),
28 p(),
29 b(param('q')), ":",
30 i($data->[$seq]->[1]), p(),
31 img({src => $data->[$seq]->[0]}),
32 p(), a({href => $data->[$seq]->[0]},
33 $data->[$seq]->[0]),
34 );
35 } elsif(param('q')) {
36 my @results =
37 Yahoo::Search->Results(
38 Image => param('q'),
39 Count => 50,
40 AllowAdult => 0,
41 );
42 if(@results) {
43 for(@results) {
44 push @$data,
45 [$_->Url(), $_->Summary()];
46 }
47 print refresh(0);
48 $cache->set(param('q'),
49 freeze($data));
50 } else {
51 print refresh(0, 1);
52 }
53 } else {
54 print h2("Slideshow Search"),
55 start_form(),
56 textfield(-name => 'q'),
57 submit(-value => "Search"),
58 end_form(),
59 font({size => 1},
60 "Powered by Yahoo! Search");
61 }
62
63 ##########################################
64 sub refresh {
65 ##########################################
66 my($sleep, $reset) = @_;
67
68 return start_html(
69 -title => "Slideshow",
70 -head => meta({
71 -http_equiv => "Refresh",
72 -content => "$sleep, URL=" .
73 ($reset ? url() : next_url())
74 }));
75 }
76
77 ##########################################
78 sub next_url {
79 ##########################################
80 my $s = param('s');
81 $s ||= 0;
82
83 return sprintf "%s?q=%s&s=%d", url(),
84 param('q'), $s+1;
85
|
|
Infos |
|---|
|
[1] Listings zu diesem Artikel: [ftp://www.linux-magazin.de/pub/listings/magazin/2005/06/Perl] [2] Yahoo Developer API Homepage: [http://developer.yahoo.com] |
|
Der |
|---|
|
|






