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

Podobne dokumenty