Грабер цен с 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, потому пока душа не лежит к такому программированию. Поверьте, всё, описанное выше, на момент написания работало корректно :)