Download: Programowanie_Perl
Transkrypt
Download: Programowanie_Perl
PROGRAMOWANIE Perl: Monitorowanie aukcji Ebay Monitorowanie aukcji Ebay przy pomocy agenta Perl i programu Jabber Ebaywatcher Jeśli chciałbyś rozpoczynać licytację na aukcjach Ebay w najbardziej odpowiednim momencie, to dlaczego nie użyć agenta Perl, który automatycznie przeszukiwałby Ebay i przy pomocy komunikatora informował o właśnie kończących się aukcjach. Dzięki temu będziesz miał przewagę nad innymi i możliwość korzystnego nabycia przedmiotów, na których ci zależy. MICHAEL SCHILLI I nternetowy serwis Ebay oferuje każdego dnia około 16 milionów przedmiotów na aukcjach elektronicznych. Rzecz jasna, z uwagi na ogromną ilość oferowanych przedmiotów trudno szybko znaleźć interesującą aukcję. Skrypt, którym zajmiemy się w tym miesiącu – ebaywatch (patrz Listing 1), będzie regularnie przesyłać zapytania do serwera Ebay i oceniać wyniki szukania zwrócone przez serwer pod kątem czasu zamknięcia danej aukcji. Gdy aukcja będzie zbliżać się do zakończenia, skrypt (używając serwera Jabber) przekaże do klienta Gaim [5] krótką informację zawierającą opis aukcji wraz z odsyłaczem URL do niej. Gaim z kolei otworzy okienko z informacją na ekranie użytkownika (patrz Rysunek 1), a po kliknięciu na wyświetlany odsyłacz użytkownik przyłączy się do aukcji w najlepszym momencie – przed jej zamknięciem, czyli wtedy, kiedy najłatwiej jest przebić cenę. W katalogu domowym użytkownika powi- 70 Luty 2004 nien znajdować się plik.ebaywatchrc, który wskazuje agentowi, jakie słowa kluczowe mają być używane do przeszukiwania serwisu Ebay. Każdy wiersz, który nie rozpoczyna się od znaku # oznacza słowo wyszukiwane, np.: #~/.ebaywatchrc dwl650 nikon oznacza, że szukamy prawdopodobnie karty WLAN D-Link „DWL-650” i dowolnych produktów firmy Nikon. Skrypt przeszukuje pola zawierające tytuły aukcji online, może również obsługiwać zaawansowane wyszukiwanie oparte na skrótach opisanych w [2]. Przykładowo wpis „photo -nikon” będzie przeszukiwał wszystkie artykuły, za wyjątkiem produktów firmy Nikon, a wpis „beatles (dvd, cd)” wyszuka wszystkie płyty CD i DVD, które mają coś wspólnego z zespołem The Beatles. www.linux-magazine.pl Moduły CPAN Jak zwykle, ktoś wpadł już wcześniej na podobny pomysł i istnieje już gotowy moduł do przeszukiwania Ebay. W archiwach CPAN można znaleźć pakiet WWW::Search::Ebay autorstwa Martina Thurn-a. Zawiera on interesujący nas moduł WWW::Search::Ebay::ByEndDate, który daje możliwość wysyłania zapytań i sortowania zwróconych rezultatów według kryterium czasu zakończenia aukcji. Inny moduł z CPAN – Net::Jabber autorstwa Ryana Eatmon-a, dostarcza kompletne API pozwalające na stworzenie funkcjonalnego klienta Jabber. Skrypt ebaywatch wykorzystuje tylko niewielką część jego potencjału. W razie potrzeby skrypt po prostu łączy się samodzielnie do serwera jabber.org na porcie 5222, autoryzuje się, wysyła wiadomość do użytkownika i odłącza się od serwera. Jeśli interesuje cię uzyskanie większej funkcjonalności w tym zakresie, więcej informacji znajdziesz w książce [3]. Perl: Monitorowanie aukcji Ebay PROGRAMOWANIE Dla uproszczenia, zarówno skrypt monitorujący jak i użytkownik, korzystają z tego samego konta na serwerze Jabber, dzięki możliwości jednoczesnego logowania z wielu klientów IM przy użyciu tej samej nazwy użytkownika. Każdy klient IM dodaje w tym celu własny tzw. resource po to, żeby rozróżnić między wieloma loginami tego samego użytkownika. Jest to łańcuch znaków, który w sposób unikalny identyfikuje każdego klienta w połączeniu z jego nazwą. Skrypt monitorujący używa w tym celu słowa ebaywatcher jako nazwy zasobu, podczas gdy Gaim definiuje swój własny łańcuch znaków. Ustawienia Sekcja konfiguracji skryptu ebaywatch zawiera w wierszu 16 zmienną $EBAY_HOST wskazującą, z którym serwerem Ebay skrypt ma się łączyć. Nasz przykładowy listing zawiera adres http://search.ebay.com. Parametr $MINS_TO_END określa, na ile minut przed końcem aukcji ma być o tym informowany użytkownik – domyślna wartość to 10 minut. Zmienna $SEEN_DB_FILE określa plik tymczasowy, w którym zapisywane są dane o statusie pomiędzy kolejnymi wyszukiwaniami. W wierszu 28 polecenie tie używa modułu DB_File do zapisu hasza %SEEN w tym pliku. Opcja O_RDWR ustawia prawa dostępu na „zapis+odczyt”, a O_CREATE przekazuje do funkcji tie() informację o tworzeniu pliku, jeśli go jeszcze nie ma. Wiersz 32 zawiera procedurę, która czyści plik w razie zakończenia pracy przez skrypt. Dla programów działających w tle (a takim jest skrypt ebaywatch), najlepszym miejscem na przechowywanie komunikatów i informacji jest plik zdarzeń. Do ich zapisywania służą funkcje DEBUG(), INFO() oraz LOGDIE() z pakietu Log::Log4perl. W naszym skrypcie używamy /tmp/ebaywatch.log jako pliku zdarzeń. Konstrukcja obiektu Ebay w wierszu 34 jest nieco niestandardowa, ponieważ używa nowej metody klasy WWW::Search, która oczekuje przekazania łańcucha Ebay::ByEndDate jako parametru. Pętla loop w wierszu 39 przetwarza plik ~/.ebaywatchrc w trybie iteracji, usuwając komentarze i puste wiersze, przekazując jednocześnie wyszukiwane słowa kluczowe do zmiennej $term. Nie przeszkadzać! Hasz %SEEN przechowuje adresy URL aukcji, o których ebaywatch już poinformował użytkownika i dla których nie wygeneruje Rysunek 1: Agent Perl-a informuje o aukcjach, które za- Rysunek 2: Tworzenie nowego kończą się w ciągu kolejnych kilku minut. użytkownika (mikes-ebaywatcher) w programie Gaim już kolejnego powiadomienia. Chodzi o to, żeby nie obciążać serwera Ebay nowymi zadaniami przeszukiwania, które i tak nie zwrócą nowych rezultatów. Skrypt ebaywatch przechowuje klucze wyszukiwania w polu notuntil/$term hasza %SEEN. Instant Messenger. Bez znaków specjalnych Wiersz 58 dokonuje konwersji znaków specjalnych do sekwencji zgodnej z notacją URL, podczas gdy wiersz 60 konstruuje zapytanie EBAYWATCH Do poprawnego działania skryptu trzeba będzie zainstalować kilka dodatkowych modułów Perl-a: WWW::Search::Ebay, Net::Jabber i Log::Log4perl. Instalację możesz wykonać przy pomocy usługi CPAN Shell: perl -MCPAN -eshell cpan>install WWW::Search::Ebay cpan>install Net::Jabber cpan> install Log::Log4perl Pierwsze dwa moduły wymagają instalacji modułów pomocniczych. Jeśli CPAN Shell zawiera ustawienie prerequisites_policy option=follow, to moduły zależne zostaną zainstalowane automatycznie. ustawienie $DEBUG będzie zapisywać większość zdarzeń, $INFO zapisze jedynie najważniejsze informacje, podczas gdy $ERROR wyłącznie błędy krytyczne. W celu uniknięcia nadmiernego przyrostu logów możesz dodać opcję RollingFileAppender do konfiguracji modułu Log::Log4perl. Dzięki temu logi będą mogły zwiększać się jedynie do określonego rozmiaru, po czym zostaną zarchiwizowane i nadpisane po przekroczeniu maksymalnej ilości plików archiwalnych (patrz [4]). Jabber Najprostszym sposobem na utworzenie konta na serwerze Jabber jest użycie proWiersz 25 skryptu ebaywatch określa pogramu Gaim (zawarty zazwyczaj w pakieziom szczegółowości informacji, które mają cie GNOME), który potrafi obsługiwać być zapisywane przez Log4perl. Domyślne większość dostępnych protokołów IM (Instant Messaging). Najnowsza wersja Gaim jest dostępna pod adresem [5]. Jeśli natomiast masz już zainstalowaną starszą wersję Gaim-a, musisz ręcznie zainstalować plug-in do obsługi Jabber-a – w tym celu wybierz plik jabberlib.so z menu Tools | Plugins (patrz Rysunek 3). Wybranie opcji Add z menu Tools | Accounts otworzy okno z formularzem – tak jak na ryRysunek 3. Agent do monitorowania Ebay-a używa sunku 2. Gaim zapamiętuje protokołu Jabber do przesyłania wiadomości. hasła i automatycznie zaloguje Starsze wersje programu Gaim wymagają instalacji się do serwera Jabber po uruchomieniu. specjalnej wtyczki. www.linux-magazine.pl Luty 2004 71 PROGRAMOWANIE Perl: Monitorowanie aukcji Ebay URL zgodnie ze składnią akceptowaną przez serwer Ebay. Pętla while w wierszu 63 używa metody next_result() do odbierania wyników wyszukiwania. Oto metody, które zwracają najważniejsze informacje o aukcji: ■ url(): adres URL aukcji ■ title(): krótki opis ■ description(): numer aukcji, ilość ofert, oferta bieżąca ■ change_date(): czas pozostający do zamknięcia aukcji (np. 2D 02H 29M) Funkcjonalność modułu WWW::Search:: Ebay::ByEndDate zapewnia, że następne aukcje bliskie zamknięcia pojawią się na pierwszych miejscach rezultatów zwracanych w pętli. Jeśli zatem wiersz 77 wykryje, że następna aukcja zakończy się w czasie dłuższym niż 10 minut, wyznaczy następne wyszukiwanie na 10 minut przed końcem au- kcji, zignoruje wszystkie aukcje o terminie zakończenia późniejszym niż znaleziona i wyjdzie z pętli. Następnie skrypt odejmuje dziesięć minut od czasu końca aukcji, zamienia tą wartość na lokalny czas uniksowy i zachowuje rezultat w notuntil/$term. Jeśli licznik $hits wciąż ma wartość 0 na końcu pętli to oznacza, że nie ma żadnych wyników wyszukiwania dla danego słowa kluczowego i kolejne wyszukiwanie tego słowa odbędzie się następnego dnia. W celu wysłania wiadomości do serwera Jabber, w wierszu 86 skrypt ebaywatch zestawia kod HTML dla odsyłacza URL ze wszystkimi dostępnymi danymi, a następnie używając wyrażeń regularnych z wiersza 90, eliminuje wszystkie niedrukowalne znaki. Funkcja jabber_send() przyjmuje następnie łańcuch znaków składający się na wiadomość i tworzy nowy obiekt Net::Jabber::Client. Po wykonaniu polecenia Connect() do serwera jabber.org, klient wysyła nazwę użytkownika i hasło (patrz ramka „Wymagania skryptu ebaywatch”). Skrypt ustawia parametr zasobów na ebaywatcher, tak jak to opisaliśmy powyżej. Wiersz 138 informuje serwer Jabber, że klient-skrypt działa, wiersz 119 zawiera funkcję, która ignoruje innych klientów: $c->SetCallBacks( Presence => sub {} ); Jabber ID zawiera nazwę użytkownika i nazwę dołączonego serwera w formacie [email protected]. Metoda wysyłająca przekazuje wiadomość, która jest przekazywana poprzez Listing 1: Używanie Jabber-a do monitorowania aukcji Ebay. 001 002 003 004 #!/usr/bin/perl ############################# # ebaywatch # Mike Schilli, 2003 ([email protected]) 005 ############################# 006 use warnings; 007 use strict; 008 009 our $JABBER_ID = "mikes-ebay-watcher"; 010 our $JABBER_PASSWD = "*******"; 011 our $JABBER_SERVER = "jabber.org"; 012 our $JABBER_PORT = 5222; 013 our $SEEN_DB_FILE = "/tmp/ebaywatch"; 014 our $EBAY_HOST = "http://search.ebay.com"; 015 our $MINS_TO_END = 10; 016 our $RC_FILE = "$ENV{HOME}/.ebaywatchrc"; 017 our %SEEN; 018 019 use Net::Jabber qw(Client); 020 use DB_File; 021 use Log::Log4perl qw(:easy); 022 use WWW::Search::Ebay; 023 024 Log::Log4perl->easy_init( 025 { level => $DEBUG, 026 file => ">>/tmp/ebaywatch.log" }); 027 028 tie %SEEN, 'DB_File', 72 Luty 2004 029 030 $SEEN_DB_FILE, O_CREAT|O_RDWR, 0755 or LOGDIE "tie: $SEEN_DB_FILE ($!)"; 031 032 END { untie %SEEN } 033 034 my $search = WWW::Search>new( 035 'Ebay::ByEndDate'); 036 open FILE, "<$RC_FILE" or 037 LOGDIE "Cannot open $RC_FILE"; 038 039 while(<FILE>) { 040 # Discard comment and empty lines 041 s/^\s*#.*//; 042 next if /^\s*$/; 043 chomp; 044 045 my $term = $_; 046 my $hits = 0; 047 048 if(exists $SEEN{"notuntil/$term"} and 049 time() < $SEEN{"notuntil/$term"}) { 050 DEBUG "Not checking '$term' until ", 051 scalar localtime 052 $SEEN{"notuntil/$term"}; 053 next; 054 } www.linux-magazine.pl 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 DEBUG "Searching for '$term'"; my $q = WWW::Search::escape_query ($term); $search->native_query($q, { ebay_host => $EBAY_HOST } ); while (my $r = $search->next_result()) { $hits++; DEBUG "Result: ", $r->url(), " ", $r->title(), " ", $r->description(), " ", $r->change_date(); if($SEEN{"url/" . $r->url()}) { 071 DEBUG "Already notified"; 072 next; 073 } 074 075 my $mins = minutes($r>change_date()); 076 077 if($mins > $MINS_TO_END) { 078 $SEEN{"notuntil/$term"} Perl: Monitorowanie aukcji Ebay obiekt Net::Jabber::Message do serwera. Serwer akceptuje wiadomość, nawet jeśli użytkownik nie pracuje w trybie online. Przyrostek GAIM wskazuje, że SendTo() nie przesyła wiadomości do klienta Jabber-a, ale do Gaim, który jest zalogowany poprzez to samo ID (patrz Rysunek 2), ustawiając nazwę zasobu na GAIM. Jeśli masz jednocześnie uruchomionego messenger-a, możesz oczekiwać kilku wiadomości od swojego wirtualnego pomocnika. Teraz wystarczy kliknąć link w otwartej wiadomości, żeby wziąć udział w aukcji! ■ Jeśli upewnisz się, że skrypt działa prawidłowo, po wywołaniu go z linii poleceń (polecenie tail -f logfile pomoże w sprawdzeniu tego) wpisz do tablicy programu cron poniższe polecenie. Sprawi ono, że skrypt będzie uruchamiany co pięć minut: AUTOR Czas */5 * * * * /home/mschilli/binU /ebaywatch Michael Schilli jest inżynierem aplikacji WWW, pracuje dla firmy AOL/Netscape w Mountain View, Kalifornia. Niedawno napisał również książkę „Perl Power” dla wydawnictwa Addison-Wesley. Można się z nim kontaktować pod adresem [email protected]. Jego strona domowa znajduje się pod adresem http://perlmeister.com. PROGRAMOWANIE INFO [1] Listing do tego artykułu: ftp://www.linux-magazin.de/pub/ listings/magazin/2004/01/Perl/ [2] David A. Karp, „eBay Hacks: 100 Industrial-Strength Tips and Tools”: O’Reilly 2003, ISBN 0-59600-564-4 [3] DJ Adams, „Programming Jabber”: O’Reilly 2002, ISBN 0-59600-202-5 [4] Automatyczne ograniczanie powiększania się logów: http://log4perl.sourceforge.net/ releases/Log-Log4perl/docs/html/Log/ Log4perl/FAQ.html#how_can_i_roll_ over_my_logfiles_automatically_ at_midnight [5] Strona domowa programu Gaim: http://gaim.sourceforge.net Listing 1: Używanie Jabber-a do monitorowania aukcji Ebay. = 079 time + ($mins $MINS_TO_END) * 60; 080 last; 081 } 082 083 INFO "Notify for ", $r->description; 084 $SEEN{"url/" . $r->url()}++; 085 086 my $msg = "<A HREF=" . $r->url() . 087 ">" . $r->title() . "</A> " . 088 "(${mins}m) " . $r->description; 089 090 $msg =~ s/[^[:print:]]//g; 091 jabber_send($msg); 092 } 093 # Pause for 1 day on no results 094 $SEEN{"notuntil/$term"} = 095 time + 24*3600 unless $hits; 096 } 097 098 ############################# 099 sub minutes { 100 ############################# 101 my($s) = @_; 102 103 my $min = 0; 104 105 $min += 60*24*$1 if $s = ~ /(\d+)[dD]/; 106 $min += 60*$1 if $s =~ /(\d+)[hH]/; 107 $min += $1 if $s = ~ /(\d+)[mM]/; 108 109 return $min; 110 } 111 112 ############################# 113 sub jabber_send { 114 ############################# 115 my($message) = @_; 116 117 my $c = Net::Jabber::Client->new(); 118 119 $c->SetCallBacks(presence => sub {}); 120 121 my $status = $c->Connect( 122 hostname => $JABBER_SERVER, 123 port => $JABBER_PORT, 124 ); 125 126 LOGDIE "Can't connect: $!" 127 unless defined $status; 128 129 my @result = $c>AuthSend( 130 username => $JABBER_ID, password => $JABBER_PASSWD, 132 resource => 'ebaywatcher', 133 ); 134 135 LOGDIE "Can't log in: $!" 136 unless $result[0] eq "ok"; 137 138 $c->PresenceSend(); 139 140 my $m = Net::Jabber::Message->new(); 141 my $jid = "$JABBER_ID" . '@' . 142 "$JABBER_SERVER/GAIM"; 143 $m->SetBody($message); 144 $m->SetTo($jid); 145 DEBUG "Jabber to $jid: $message"; 146 my $rc = $c->Send($m, 1); 147 148 $c->Disconnect; 149 } 131 www.linux-magazine.pl Luty 2004 73