Грабер цен с s7.ru

30 августа 2009

Задача

Цель: написание робота собирающего цены по которым можно забронировать билеты на сайте www.s7.ru 1. заходите на сайт 2. выбираете направление (например, москва-уфа) 3. выбираете "в одну сторону" 4. выбираете дату отправления (первоначально - сегодняшнюю, т.е. дату проверки). 5. кликаете далее Получаете таблицу, в которой приведены 1. рейсы 2. цены по которым можно забронировать билет. из них выбрать минимальную. Внимание - иногда список рейсов может менятся, одбавляться и тп - следовательно - необходимо отслеживать номер рейса, а не просто брать сверху вниз. Записывать всё в результирующую таблицу: Направление Дата отправления Номер рейса Цена Далее, необходимо перейти на следующий день, для этого нажать на стрелочку "направо", и т.д. Внимание - иногда сессия прерывается. В таком случае необходимо сгенерировать запрос заново.

Источник: weblancer.net

Разбиваем задачу на части

1. Получить с сервера данные.
2. Вытащить из этих данных нужную нам информацию по рейсам и стоимостям.
3. Обеспечить рабочий скрипт каким-то окружением, которое будет впоследствии задавать режим его работы.
4. Сделать вывод результатов в удобной для пользователя форме.

Получение данных

Основная страница поиска расположена на дополнительном домене: http://ibe.s7.ru/S7/webqtrip.html. С этой страницей и будем работать. Отслеживаем посредством Firebug, что происходит при поиске билетов.

Ищем рейсы Москва—Уфа на 10 августа 2009 года; коды аэропортов вытаскиваем из javascript'а страницы поиска.

После нескольких попыток-итераций получается примерно такой код.

  • #!/usr/bin/perl
  • use strict;
  • use LWP::UserAgent;
  • my $url = 'http://ibe.s7.ru/S7/webqtrip.html';
  • my %post_params = (
  • origin => 'MOW',
  • destination => 'UFA',
  • departureDate => '2009-08-05',
  • cabinClass => 'E', # 'E' - Econom class; 'B' - Business class
  • journeySpan => 'OW', numAdults => '1', numChildren => '0', numInfants => '0',
  • _eventId => 'submit', DIRECTION => 'SUBMIT', searchTypeSelected => 'NORMAL',
  • );
  • my $ua = LWP::UserAgent->new;
  • $ua->agent('Search minimal price bot; programming: raskumandrin@gmail.com');
  • $ua->cookie_jar({});
  • $ua->proxy(['http'], 'http://proxy.server.com:3128');
  • push @{$ua->requests_redirectable},'POST';
  • my $init_session = $ua->get($url);
  • ($post_params{_flowExecutionKey}) =
  • ($init_session->content =~ m{name="_flowExecutionKey" value="([^"]*)}m);
  • my $response = $ua->post($url,\%post_params);
  • open HTML,'>','response.html';
  • print HTML $response->content;
  • close HTML;

Отлично. В файле response.html получаем такую же табличку с рейсами, какую получаем и при запросе посредством браузера (ну разве что с немного покоцаными стилями).

Разбор данных

Откуда-куда и дата у нас уже есть. Нужно собрать номера рейсов и минимальную стоимость.

  • #!/usr/bin/perl
  • use strict;
  • use TagParser;
  • my $html = HTML::TagParser->new('response.html');
  • my @inputs_s7 = $html->getElementsByAttribute( "id", "S7" );
  • foreach my $input_s7 (@inputs_s7) {
  • my $input_s7_attr = $input_s7->attributes();
  • my @flight_elems = $html->getElementsByAttribute('href',
  • "javascript:showFlightInfo('$input_s7_attr->{value}');");
  • my ($outbound) = ($input_s7_attr->{class} =~ m{(.*) hide});
  • my $flight = $flight_elems[1]->innerText();
  • $flight =~ s{ }{ };
  • print "Flight: '$flight'; outbound code: $outbound\n";
  • foreach my $fare_type qw (PO LE TE LI) {
  • my $price_elem =
  • $html->getElementById("priceOfFareLabel_outbound_${outbound}_$fare_type");
  • if ($price_elem) {
  • my ($price) = ($price_elem->innerText() =~ m{(.*) RUB});
  • print "$fare_type: $price RUB\n";
  • }
  • else {
  • print "$fare_type: -\n";
  • }
  • }
  • }

Этот код разбирает html с привлечением модуля TagParser, и выводит табличку рейсов-тарифов с ценами.

5 сентября 2009 года

Вообще-то тут надо привести работающий примерчик. Как один из вариантов — набросать обработчик формы на jQuery. Но JavaScript же не такой гармоничный и изящный как Perl, потому пока душа не лежит к такому программированию. Поверьте, всё, описанное выше, на момент написания работало корректно :)