Поиск реальных авиарейсов

Разобравшись с тестовыми данными, попробуем применить те же самые методы оптимизации к реальным данным об авиарейсах. Мы загрузим данные с сайта Kayak, который предоставляет API для поиска авиарейсов. Основное отличие тестовых данных от реальных заключается в том, что между крупными городами бывает гораздо больше девяти рейсов в день.

API сайта Kayak

Сайт Kayak, изображенный на рис. 5.6, – это популярная вертикальная поисковая система, предназначенная для путешественников. В Сети

Рис. 5.6. Интерфейс системы поиска маршрутов Kayak

много сайтов подобного рода, но Kayak полезен тем, что предоставляет удобный API, написанный на XML, которым можно воспользоваться из программы на языке Python для поиска реальных маршрутов. Чтобы получить доступ к API, вам потребуется зарегистрироваться для получения ключа разработчика, зайдя на страницу http://www.kayak. com/labs/api/search.

Ключ разработчика представляет собой длинную строку букв и цифр, которую необходимо указывать при поиске авиарейсов с помощью системы Kayak (он годится и для поиска гостиниц, но эту тему мы обсуждать не будем). В настоящий момент не существует специального API на языке Python для сайта Kayak, похожего на API для сайта del.icio. us, но структура XML-документа очень хорошо описана.

Пакет minidom

Пакет minidom – часть стандартного дистрибутива Python. Это простая реализация интерфейса Document Object Model (DOM), который позволяет представить произвольный XML-документ в виде дерева объектов. Пакет принимает на входе строку или открытый файл, содержащий XML-документ, и возвращает объект, позволяющий легко извлекать нужную информацию. Введите, например, следующие команды в интерактивном сеансе: >>> import xml.dom.minidom

>>> dom=xml.dom.minidom.parseString(‘<data><rec>Hello!</rec></data>’) >>> dom

<xml.dom.minidom.Document instance at 0x00980C38> >>> r=dom.getElementsByTagName(‘rec’)

>>> r

[<DOM Element: rec at 0xa42350>] >>> r[0].firstChild <DOM Text node "Hello!"> >>> r[0].firstChild.data

u’Hello!’

Поскольку сейчас многие сайты предлагают XML-интерфейс для доступа к информации, умение обращаться с Python-пакетами для работы с XML окажется весьма полезным для программирования коллективного разума. Вот сводка наиболее важных методов DOM-объектов, которыми мы будем пользоваться в процессе доступа к данным с помощью Kayak API: getElementsByTagName(name)

Возвращает список узлов DOM, соответствующих элементам с тегом name. firstChild

Возвращает первый дочерний узел данного объекта. В примере выше первым дочерним узлом узла r является узел, представляющий текст Hello.

data

Возвращает данные, ассоциированные с объектом. В большинстве случаев это строка в кодировке Unicode, содержащаяся внутри узла.

Поиск авиарейсов

Начнем с создания нового файла kayak.py, в который следует включить такие предложения:

import time

import urllib2

import xml.dom.minidom

kayakkey=’ВАШ_КЛЮЧ’

Прежде всего нужно написать код открытия нового сеанса работы с сайтом Kayak. Он будет посылать запрос странице apisession, задавая в качества параметра token полученный вами ключ разработчика. В ответ возвращается XML-документ, содержащий тег sid, внутри которого находится идентификатор сеанса:

<sid>1-hX4lII_wS$8b06a07kH]</sid> Ваша функция должно просто разобрать этот документ и извлечь содержимое тега sid. Добавьте ее в файл kayak.py: def getkayaksession( ):

#   Создаем URL для открытия нового сеанса

url=’http://www.kayak.com/k/ident/apisession?token=%s&version=1′ % kayakkey

#   Разбираем полученный XML-документ

doc=xml.dom.minidom.parseString(urllib2.urlopen(url).read( ))

#   Ищем элемент <sid>xxxxxxxx</sid> sid=doc.getElementsByTagName(‘sid’)[0].firstChild.data return sid

Далее необходима функция, которая отправит запрос на поиск авиарейсов. Соответствующий URL очень длинный, поскольку содержит все параметры поиска. Самыми важными являются параметры sid (идентификатор сеанса, возвращенный функцией getkayaksession), destination (пункт назначения) и depart_date (дата вылета).

В XML-документе, возвращенном в ответ на этот запрос, имеется тег searchid, который функция извлекает точно так же, как это делает getkayaksession. Поскольку поиск может занять много времени, этот запрос не выдает результатов, он лишь начинает поиск и возвращает идентификатор, с помощью которого впоследствии можно периодически справляться о готовности результатов. Добавьте в файл kayak.py такой код:

def flightsearch(sid,origin,destination,depart_date):

#   Создаем URL запроса на поиск

url=’http://www.kayak.com/s/apisearch?basicmode=true&oneway=y&origin=%s’ % origin

url+=’&destination=%s&depart_date=%s’ %% (destination,depart_date) url+=’&return_date=none&depart_time=a&return_time=a’ url+=’&travelers=1&cabin=e&action=doFlights&apimode=1′ url+=’&_sid_=%s&version=1′ %% (sid)

#       Получаем в ответ XML

doc=xml.dom.minidom.parseString(urllib2.urlopen(url).read( ))

#       Извлекаем идентификатор поиска

searchid=doc.getElementsByTagName(‘searchid’)[0].firstChild.data return searchid

Наконец нам понадобится функция, которая запрашивает результаты до тех пор, пока у сервера еще что-то для нас осталось. Для получения результатов Kayak предоставляет еще один URL – flight. В возвращенном XML-документе имеется тег morepending. Если он содержит слово true, то имеются еще результаты. Функция должна обращаться к странице до тех пор, пока тег morepending содержит слово true, а затем вернуть полный набор результатов. Добавьте ее в файл kayak.py:

def flightsearchresults(sid,searchid):

#       Удалить начальный $ и запятые и преобразовать строку в число с плавающей

#       точкой

def parseprice(p):

return float(p[1:].replace(‘,’,”))

#       Цикл опроса while 1:

time.sleep(2)

# Создаем URL для опроса url=’http://www.kayak.com/s/basic/flight?’

url+=’searchid=%s&c=5&apimode=1&_sid_=%s&version=1′ % (searchid,sid) doc=xml.dom.minidom.parseString(urllib2.urlopen(url).read( ))

# Ищем тег morepending и продолжаем, пока он содержит слово true morepending=doc.getElementsByTagName(‘morepending’)[0].firstChild if morepending==None or morepending.data==’false’: break

#       Теперь загружаем весь список url=’http://www.kayak.com/s/basic/flight?’

url+=’searchid=%s&c=999&apimode=1&_sid_=%s&version=1′ % (searchid,sid) doc=xml.dom.minidom.parseString(urllib2.urlopen(url).read( ))

#       Представляем различные элементы в виде списков prices=doc.getElementsByTagName(‘price’) departures=doc.getElementsByTagName(‘depart’) arrivals=doc.getElementsByTagName(‘arrive’)

#       Объединяем их

return zip([p.firstChild.data.split(‘ ‘)[1] for    p in departures],

[p.firstChild.data.split(‘ ‘)[1] for             p in arrivals],

[parseprice(p.firstChild.data) for p             in prices])

Обратите внимание, что на последнем шаге функция формирует три списка, соответствующих элементам с тегами price, depart и arrive. Количество таких элементов одинаково – по одному для каждого рейса, поэтому можно воспользоваться функцией zip, которая объединит все три списка в один, элементами которого будут кортежи из трех элементов. Информация о вылете и прилете представлена в виде даты и времени, которые разделены пробелом, поэтому мы вызываем метод split, чтобы выделить только время. Кроме того, цена билета преобразуется в число с плавающей точкой с помощью вспомогательной функции parseprice.

Выполните поиск реальных авиарейсов в интерактивном сеансе, чтобы убедиться, что все работает (не забудьте изменить дату, указав какой- нибудь день в будущем):

>>> import kayak

>>> sid=kayak.getkayaksession( )

>>> searchid=kayak.flightsearch(sid,’BOS’,’LGA’,’11/17/2006′) >>> f=kayak.flightsearchresults(sid,searchid) >>> f[0:3]

[(u’07:00′, u’08:25′, 60.3), (u’08:30′, u’09:49′, 60.3), (u’06:35′, u’07:54′, 65.0)]

Список рейсов отсортирован по цене, а рейсы с одинаковой стоимостью – по времени. Это хорошо, так как похожие решения оказываются рядом. Чтобы интегрировать этот код с написанным ранее, нам нужно создать полное расписание для всех членов семейства Глассов, сохранив ту же структуру, что и в тестовом файле. Для этого требуется лишь перебрать людей в списке и выполнить для каждого из них поиск рейсов туда и обратно. Добавьте в файл kayak.py функцию createschedule: def createschedule(people,dest,dep,ret): # Получить идентификатор сеанса для выполнения поиска sid=getkayaksession( ) flights={}

for p in people: name,origin=p

#       Рейс туда

searchid=flightsearch(sid,origin,dest,dep) flights[(origin,dest)]=flightsearchresults(sid,searchid)

#       Рейс обратно

searchid=flightsearch(sid,dest,origin,ret) flights[(dest,origin)]=flightsearchresults(sid,searchid)

return flights

Теперь можно попытаться оптимизировать рейсы для членов семьи, пользуясь реальными данными. Поиск на сайте Kayak занимает довольно много времени, поэтому для начала ограничимся лишь первыми двумя членами. Введите в интерактивном сеансе следующие команды: >>> reload(kayak)

>>> f=kayak.createschedule(optimization.people[0:2],’LGA’,

… ’11/17/2006′,’11/1Q/2006′) >>> optimization.flights=f >>> domain=[(0,30)]*len(f)

>>> optimization.geneticoptimize(domain,optimization.schedulecost)

770.0 703.0

>>> optimization.printschedule(s)

Seymour BOS 16:00-17:20 $85.0 1Q:00-20:28 $65.0 Franny DAL 08:00-17:25 $205.0 18:55-00:15 $133.0

Примите поздравления! Вы только что выполнили оптимизацию на реальных данных об авиарейсах. Пространство поиска теперь гораздо больше, так что имеет смысл поэкспериментировать с параметрами – максимальной температурой и скоростью обучения. Эти идеи можно развить во многих направлениях. Можно добавить поиск на метеорологических сайтах, чтобы оптимизировать сочетание цены и температуры в пункте назначения. Или включить поиск гостиниц, чтобы найти пункт назначения с разумным сочетанием цен на авиабилеты и проживание. В Интернете есть тысячи сайтов, предоставляющих путешественникам данные, которые можно оптимизировать. API сайта Kayak налагает ограничение на количество поисков в течение суток, но возвращает прямые ссылки на сайты, где можно заказать билеты на самолет или забронировать номер в гостинице, а значит, вы легко сможете включить этот API в состав любого приложения.

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100