Если вы когда-нибудь пробовали тратить баллы по кредитной карте на бесплатные авиабилеты, возможно, вы сможете подтвердить, что это непростой подвиг. Когда вы думаете, что эти плоские троны, обтянутые кожей, впереди вас, в декорациях, реальность закрытых дат, мимолетного места для наград и «приоритетных» цен. Для многих это просто борьба силы воли, когда вы просматриваете результаты изо дня в день, пока либо не найдете ту неуловимую заставку, либо ваши пальцы не сломаются ...

Но что, если бы вам не приходилось часами быть привязанными к компьютеру? Какие новые маршруты вы могли бы изучить, если проверка наличия награды заняла всего несколько секунд, а не несколько дней? С помощью таких инструментов, как Headless Chrome, Puppeteer, Node, React и D3.js, я смог это сделать.

Мой обычный процесс бронирования премиальных рейсов состоял из ежедневного ручного поиска и записи результатов в Evernote. Для предстоящей поездки нашей семьи в Азию это оказалось слишком медленным и подверженным ошибкам, поэтому я создал инструмент для его автоматизации: Flightplan.

Цифры отрезвляют. Используя Flightplan, я смог проанализировать более 40 тысяч премиальных билетов (41 839, если быть точным) для четырех разных авиакомпаний: Cathay Pacific, All Nippon Airways, Korean Air и Singapore Air. Мы выкупили полеты на сумму более 90 тысяч долларов для 6 пассажиров, посетивших 6 разных городов в трех разных салонах (первый, бизнес-класс и премиум-эконом), всего за чуть менее 1 миллиона миль. Flightplan имел решающее значение для поиска лучших наград, пресловутой иголки в стоге сена.

Выберите ваше оружие…

Как любой хороший хакер-путешественник, прежде чем создавать новый инструмент, я сначала опробовал все существующие инструменты. KVS Tool и ExpertFlyer незаменимы, но они только ускорили процесс, а не заменили мой ручной процесс. С помощью KVS Tool вы можете искать до 30 дней одновременно, но он останавливается, как только будет найден результат. ExpertFlyer позволяет выполнять поиск за 7 дней одновременно и работает довольно быстро, но не поддерживает все авиакомпании. Ни один из этих инструментов не суммирует за вас результаты, поэтому мне все равно нужно было где-то записывать их вручную.

Я пробовал другие инструменты, такие как Award Nexus и Award.flights, но применялись те же предостережения, что и выше. Мой идеальный инструмент:

  • Работайте в автономном режиме часами без какого-либо вмешательства.
  • Объедините все данные о наградах в базу данных.
  • Разрешить визуализацию наград в реальном времени, чтобы я мог планировать свои поездки.
  • Запускайте в Windows, Mac или Linux (я использую все три).
  • Быть почти неотличимым от настоящего человека для оператора веб-сайта.
  • Изящно обрабатывайте неудавшиеся запросы.
  • Наконец, используйте открытый исходный код, чтобы другие могли внести свой вклад, и вам не пришлось ждать, пока один человек починит инструмент, когда он сломается из-за изменения веб-сайта.

Так как ничего даже отдаленно близкого не существовало, я решил построить его сам.

Хозяин Марионеток

Авиакомпании часто используют игру в кошки-мышки, пытаясь заблокировать автоматические скребки, пропуская при этом настоящих людей. И последняя версия мыши - это Headless Chrome, который похож на настоящий Chrome, но его можно автоматизировать и запускать headless (без видимого окна). Поскольку Chrome кроссплатформенный, Chrome без головы тоже.

Для управления Headless Chrome я использовал библиотеку под названием Puppeteer. Также созданный Google, он позволяет вам управлять Headless Chrome, написав JavaScript. Мы также можем написать произвольный JavaScript, который будет работать в нашем экземпляре Chrome, что позволит нам делать все, что затрудняет обнаружение парсера.

Имея это в виду, я использовал Node.js для написания сценария, который запускал бы поиск по наградам и записывал результаты в базу данных SQLite вместе с необработанным HTML. Puppeteer даже позволяет нам легко сохранять скриншоты, которые необходимы для отладки.

Наконец, мы готовы извлечь данные о награде из сохраненных файлов HTML. CheerioJS позволяет легко загружать файл в память и анализировать DOM. Результаты награды также можно сохранить в базе данных.

Оживление данных

Дело не в количестве пустого пространства, а в том, как оно используется. Дело не в том, сколько информации есть, а в том, насколько эффективно она организована.
- Эдвард Р. Тафт

Имея все данные о наградах, мне нужен был способ визуализировать их. Я читал увлекательную статью, опубликованную участником FlyerTalk Влада Гутковским: Визуализация наград Сингапура с помощью радарных диаграмм. Хотя решение Влада по духу было похоже на то, что я делал, к сожалению, решение Влада требовало платного программного обеспечения и уже было заблокировано Singapore Air.

Тем не менее его предложение было убедительным: вместо размещения данных в календаре, радиолокационная диаграмма могла бы лучше выделить закономерности в данных. Поговорим о нестандартном мышлении! (И внутри круга, я полагаю ...)

Изучая схемы радиального календаря, я наткнулся на фантастическую работу Bureau Oberhaeuser:

D3.js казался естественным, чтобы воплотить в жизнь такую ​​классную концепцию. Я даже нашел уже работающую версию на Github (любезно предоставлено Родни Левитоном). В реализации Родни использовался Angular, тогда как я больше работал с React, поэтому я переписал его, чтобы лучше поддержать свой сценарий. Я добавил форму поиска в приложение React и процесс Node.js, который действует как сервер API перед базой данных SQLite.

Итак, конечный результат - это то, что вы видите ниже:

Загляните в Репозиторий Flightplan на Github или установите его через npm:

npm i flightplan-tool

Хотя все инструменты включены в комплект для запуска всего, что я показал здесь, вы также можете использовать Flightplan в качестве библиотеки для парсинга ваших собственных скриптов, например:

const fp = require('flightplan');
const cx = fp.new('cx');
await cx.initialize({ username: '...', 'password': '...' });
const { responses } = await cx.search({
  fromCity: 'HKG', toCity: 'LHR',
  departDate: '2019-03-06', cabin: 'first'
});
const { awards } = cx.parse(responses);

Надеюсь, это поможет вам спланировать следующую большую поездку, для меня это определенно был образовательный месяц. Что бы вы хотели добавить в Flightplan дальше? Дайте мне знать!