Google закрыла свой Flights API ещё в 2018 году, а цены на авиабилеты по-прежнему меняются — до 17 раз за 48 часов по одному внутреннему маршруту. Если тебе нужен программный доступ к этим данным, парсинг по сути остаётся единственным реальным вариантом.
Я потратил немало времени на тестирование разных способов получения данных о перелётах из Google, и ситуация сильно изменилась — особенно после того, как Google внедрила SearchGuard в январе 2025 года. В этом руководстве я покажу, как собрать рабочий Python-скрипт для Google Flights с помощью Playwright, как обойти антибот-защиту, которая ломает большинство решений, а затем как превратить всё это в автоматический трекер цен с уведомлениями. Если ты не хочешь писать код, я также покажу вариант без кода с Thunderbit, который даёт тот же результат примерно за две минуты.
Зачем парсить Google Flights на Python?
Google Flights — один из лидеров в поиске авиабилетов. На мобильных устройствах в США его видимость взлетела до 47,6%, обойдя все крупные OTA-сервисы. Рынок туристического метапоиска, на котором он работает, оценивается в $8,33 млрд в 2024 году и растёт на 30,2% CAGR. Но после того как QPX Express API навсегда отключили 10 апреля 2018 года, официального способа получать эти данные программно больше нет.
При этом цены на авиабилеты могут колебаться до 50% даже на одном и том же маршруте, а средний разброс между минимальной и максимальной ценой составляет около $20. Такие авиакомпании, как Delta, используют 77 тарифных корзин для динамического ценообразования. Средняя цена перелёта туда-обратно по США в начале 2026 года составляет $408, а стоимость билетов на 14,9% выше, чем годом ранее.
Доминирующая платформа, отсутствие API, нестабильные цены — именно поэтому парсинг Google Flights на Python стал одним из самых популярных проектов на GitHub и в туристических сообществах.
Вот кому это полезно и зачем:
| Тип пользователя | Сценарий использования | Главная выгода |
|---|---|---|
| Индивидуальные путешественники | Отслеживать цены по конкретным маршрутам | Экономия в среднем $50 на перелёт |
| Туристические агентства | Анализ конкурентных цен | Мониторинг паритета тарифов в реальном времени |
| Корпоративные travel-команды | Оптимизация расходов по маршрутам | Экономия 10–30% на деловых поездках |
| Разработчики | Создавать приложения для сравнения тарифов | Программный доступ к ценовым данным |
| Исследователи | Анализ волатильности цен авиакомпаний | Академические и рыночные исследования |
Пользователи форумов говорят об этом прямо: «Google Flights API был отключён, значит, нужно использовать web scraping» — такая мысль встречается снова и снова. И отдача от этого реальная — Hopper утверждает, что точность прогнозов достигает 95% при анализе более 5 млрд ценовых предложений в день, а данные Expedia за 2026 год показывают, что бронирование за 8–15 дней до вылета экономит около $25 на внутренних рейсах.
Какие данные можно получить из Google Flights?
Страница результатов Google Flights содержит на удивление богатый набор полей. Обычно доступны такие данные:
- Название авиакомпании (и логотип)
- Время вылета и код аэропорта
- Время прибытия и код аэропорта
- Общая длительность перелёта
- Количество пересадок и сведения о стыковках (аэропорт, длительность, ночная ли)
- Цена билета (в зависимости от валюты)
- Выбросы CO2 (кг CO2e, с процентной разницей относительно типичных рейсов)
- Класс обслуживания, номер рейса, модель самолёта
- Параметры пространства для ног
- Дополнительные удобства (Wi‑Fi, розетки, потоковое видео)
- Индикатор ценового уровня (низкий/обычный/высокий)
- Предупреждения о задержках (например, «часто задерживается более чем на 30 минут»)
Набор данных зависит от маршрута, даты и типа билета (в одну сторону или туда-обратно). Вот как может выглядеть одна запись о перелёте в JSON:
{
"search_date": "2026-04-16",
"route": "SFO-JFK",
"departure_date": "2026-05-15",
"flights": [
{
"airline": "United Airlines",
"flight_number": "UA123",
"departure_time": "08:00",
"departure_airport": "SFO",
"arrival_time": "16:35",
"arrival_airport": "JFK",
"duration_minutes": 335,
"stops": 0,
"price_usd": 287,
"price_level": "low",
"co2_kg": 156,
"co2_vs_typical": "-12%",
"travel_class": "Economy"
}
]
}
Подготовка Python-среды
Прежде чем писать код парсинга, нужно подготовить несколько вещей.
Что понадобится:
- Уровень сложности: средний
- Время: примерно 1–2 часа на весь туториал
- Что нужно: Python 3.7+, базовые знания Python, браузер на базе Chrome
Установка нужных библиотек
Мы будем использовать Playwright для автоматизации браузера (Google Flights на 100% отрисовывается JavaScript’ом — обычный HTTP-запрос ничего полезного не вернёт), а также несколько вспомогательных пакетов:
pip install playwright playwright-stealth pandas
playwright install chromium
- Playwright — автоматизация headless-браузера, обработка JavaScript и встроенные механизмы ожидания
- playwright-stealth — маскирует типичные сигналы бота
- pandas — для анализа данных и последующего экспорта в CSV
Почему Playwright лучше Selenium или requests
Google Flights не работает только с requests + BeautifulSoup — весь контент страницы рендерится JavaScript’ом. Нужен настоящий браузер.
| Функция | Playwright | Selenium | requests + BS4 |
|---|---|---|---|
| Отрисовка JS | Полная поддержка | Полная поддержка | Нет |
| Скорость | На 42% быстрее в среднем | Базовый уровень | Неприменимо |
| Поддержка async | Нативная | Только последовательный режим | Неприменимо |
| Потребление памяти | На 30% меньше | Выше | Минимальное |
| Обход антибот-защиты | Хорошо (со stealth) | Легче обнаружить | Неприменимо |
Playwright быстрее, современнее и лучше работает с async. Для Google Flights это очевидный фаворит.
Пошагово: как парсить Google Flights на Python
Это основная часть руководства. Мы соберём парсер поэтапно.

Шаг 1: Определите классы данных
Начни со структуры параметров поиска и данных о рейсе через dataclass’ы Python. Это сделает код чище и упростит дальнейшее расширение.
from dataclasses import dataclass, field
from typing import Optional, List
@dataclass
class SearchParams:
origin: str # например, "SFO"
destination: str # например, "JFK"
departure_date: str # например, "2026-05-15"
return_date: Optional[str] = None
trip_type: str = "one-way" # "one-way" или "round-trip"
travel_class: str = "economy"
@dataclass
class FlightData:
airline: str = ""
departure_time: str = ""
arrival_time: str = ""
duration: str = ""
stops: str = ""
price: str = ""
co2_emissions: str = ""
Каждое поле напрямую соответствует тому, что мы будем извлекать со страницы. Если сразу задать такую структуру, потом не придётся таскать по коду неаккуратные словари.
Шаг 2: Разберись со структурой URL Google Flights
Google Flights кодирует параметры поиска с помощью Base64-encoded Protobuf в URL-параметре tfs. Можно попробовать reverse engineering этого формата, а можно пойти более простым путём и сформировать URL с обычным текстовым запросом.
Самый простой вариант — использовать формат поискового запроса:
https://www.google.com/travel/flights?q=flights+from+SFO+to+JFK+on+2026-05-15&curr=USD
Если нужен больший контроль, URL можно собирать программно:
def build_flights_url(origin: str, destination: str, date: str) -> str:
base = "https://www.google.com/travel/flights"
query = f"flights from {origin} to {destination} on {date}"
return f"{base}?q={query.replace(' ', '+')}&curr=USD"
Альтернатива — взламывать Protobuf-кодирование. Это даёт более точный контроль, но ломается, когда Google меняет внутренний формат. Такие библиотеки, как fast-flights на GitHub, используют декодирование Protobuf и обходят HTML-парсинг полностью, но это уже более продвинутый подход.
Шаг 3: Запусти браузер и открой Google Flights
Ниже — конфигурация Playwright. Мы используем playwright-stealth, чтобы снизить риск детекта с самого начала.
import asyncio
from playwright.async_api import async_playwright
from playwright_stealth import Stealth
async def scrape_flights(params: SearchParams) -> List[FlightData]:
async with Stealth().use_async(async_playwright()) as pw:
browser = await pw.chromium.launch(
headless=True,
args=[
"--disable-blink-features=AutomationControlled",
"--disable-dev-shm-usage",
"--no-first-run",
]
)
context = await browser.new_context(
viewport={"width": 1920, "height": 1080},
user_agent=(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/125.0.0.0 Safari/537.36"
),
locale="en-US",
timezone_id="America/New_York",
)
# Заранее задаём cookie согласия, чтобы пропустить всплывающее окно
await context.add_cookies([{
"name": "SOCS",
"value": "CAESHwgBEhJnd3NfMjAyNTAyMjctMF9SQzIaBXpoLUNOIAEaBgiAy6O-Bg",
"domain": ".google.com",
"path": "/"
}])
page = await context.new_page()
Мы запускаем headless-режим для продакшена (для отладки переключи на headless=False), задаём реалистичные viewport и user-agent, а также заранее ставим cookie SOCS, чтобы пропустить окно согласия — об этом подробнее в разделе про антибот-защиту.
Шаг 4: Перейди к результатам поиска
Загрузи сформированный URL и дождись появления результатов:
url = build_flights_url(
params.origin, params.destination, params.departure_date
)
await page.goto(url, wait_until="networkidle")
# Ждём, пока загрузятся результаты рейсов
await page.wait_for_selector(
"li.pIav2d", timeout=15000
)
Если здесь возникает таймаут, обычно причина одна из двух: либо всплывающее окно согласия блокирует страницу (см. исправление через cookie в шаге 3), либо Google выдал CAPTCHA. Оба случая мы разберём ниже.
Шаг 5: Подгрузи все результаты
Google Flights скрывает дополнительные варианты за кнопкой «Show more flights». Нужно нажимать её, пока не станут видны все рейсы:
# Нажимаем "Show more flights", пока не загрузятся все результаты
while True:
try:
more_button = page.locator(
'button:has-text("Show more flights")'
)
if await more_button.is_visible(timeout=3000):
await more_button.click()
await page.wait_for_timeout(2000)
else:
break
except Exception:
break
Этот цикл нажимает кнопку, ждёт 2 секунды, пока подгрузятся новые результаты, и прекращает работу, когда кнопка исчезает. По моим тестам, на большинстве маршрутов бывает 1–3 страницы результатов.
Шаг 6: Извлеки данные о рейсах через CSS-селекторы
Теперь парсим реальные данные с загруженной страницы. Вот селекторы, проверенные на апрель 2026 года — ниже в разделе поддержки мы ещё вернёмся к тому, почему дата проверки так важна:
flights = []
cards = await page.query_selector_all("li.pIav2d")
for card in cards:
flight = FlightData()
# Название авиакомпании
airline_el = await card.query_selector(
"div.sSHqwe span:not([class])"
)
if airline_el:
flight.airline = (await airline_el.inner_text()).strip()
# Время вылета
dep_el = await card.query_selector(
'span[aria-label*="Departure time"]'
)
if dep_el:
flight.departure_time = (await dep_el.inner_text()).strip()
# Время прибытия
arr_el = await card.query_selector(
'span[aria-label*="Arrival time"]'
)
if arr_el:
flight.arrival_time = (await arr_el.inner_text()).strip()
# Длительность
dur_el = await card.query_selector("div.gvkrdb")
if dur_el:
flight.duration = (await dur_el.inner_text()).strip()
# Пересадки
stops_el = await card.query_selector("div.EfT7Ae span")
if stops_el:
flight.stops = (await stops_el.inner_text()).strip()
# Цена
price_el = await card.query_selector(
"div.FpEdX span"
)
if price_el:
flight.price = (await price_el.inner_text()).strip()
# Выбросы CO2
co2_el = await card.query_selector("div.O7CXue")
if co2_el:
flight.co2_emissions = (
await co2_el.get_attribute("aria-label") or ""
).strip()
flights.append(flight)
await browser.close()
return flights
Важно: классы вроде pIav2d, sSHqwe и FpEdX генерируются Closure Compiler от Google и могут измениться в любой момент. Селекторы на основе aria-label гораздо стабильнее. Ниже я покажу полноценную стратегию поддержки.
Шаг 7: Сохрани результаты в JSON или CSV
И наконец, сохраняем данные с меткой времени — это критично, если позже ты захочешь отслеживать цены:
import json
from datetime import datetime
from dataclasses import asdict
async def main():
params = SearchParams(
origin="SFO",
destination="JFK",
departure_date="2026-05-15"
)
flights = await scrape_flights(params)
output = {
"search_date": datetime.now().isoformat(),
"params": asdict(params),
"flights": [asdict(f) for f in flights],
}
with open("flights.json", "w") as f:
json.dump(output, f, indent=2)
# Также сохраняем в CSV
import pandas as pd
df = pd.DataFrame([asdict(f) for f in flights])
df["search_date"] = datetime.now().isoformat()
df["route"] = f"{params.origin}-{params.destination}"
df.to_csv("flights.csv", index=False)
print(f"Scraped {len(flights)} flights")
asyncio.run(main())
Запусти это — и у тебя появятся flights.json и flights.csv с результатами. По моим тестам, поиск SFO-JFK обычно возвращает 30–80 вариантов и занимает около 15–20 секунд.
Антибот-гайд для парсинга Google Flights
Большинство туториалов заканчиваются здесь. Большинство парсеров — тоже. В январе 2025 года Google запустила SearchGuard, и это за одну ночь сломало почти все SERP-парсеры. Google описывает это как «результат десятков тысяч человеко-часов и миллионов долларов инвестиций». По оценке scraperly.com, сложность парсинга Google Flights — 4 из 5.
Ни один материал конкурентов не разбирает это глубоко, хотя именно из-за этого у людей чаще всего перестают работать парсеры. Вот с чем ты столкнёшься и как это обходить.

Случайные задержки между запросами
Самая простая защита от rate limiting. Две строки кода, средняя эффективность:
import time
import random
time.sleep(random.uniform(3, 7))
Добавляй это между переходами страниц. Фиксированные интервалы вроде ровно 5 секунд каждый раз — подозрительный сигнал, лучше рандомизировать.
Ротация User-Agent
Отправлять одну и ту же строку user-agent в каждом запросе слишком легко отследить. Меняй её из списка:
import random
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/125.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 Safari/605.1.15",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0",
]
user_agent = random.choice(USER_AGENTS)
Обход headless-детекта
Google проверяет флаг navigator.webdriver и другие признаки автоматизации. Библиотека playwright-stealth закрывает большую часть таких сигналов, но также стоит задать launch arguments, показанные в шаге 3. Ключевые флаги:
args=[
"--disable-blink-features=AutomationControlled",
"--disable-dev-shm-usage",
"--no-first-run",
]
Это помогает пройти базовую проверку. SearchGuard идёт глубже — анализирует скорость движения мыши, задержки ввода с клавиатуры и паттерны прокрутки, — но при умеренных объёмах парсинга stealth-режим плюс реалистичные задержки обычно хватает.
Ротация прокси: дата-центры vs. residential
Если ты делаешь больше нескольких запросов, понадобятся прокси. Разница существенная:
| Характеристика | Дата-центровые прокси | Residential-прокси |
|---|---|---|
| Скорость | 100–1,000 Mbps | 10–100 Mbps |
| Процент успешных запросов (Google) | 20–40% | 85–95% |
| Стоимость | $0.10–$0.50/IP в месяц | $5–$15/GB |
| Риск обнаружения | Высокий | Очень низкий |
Residential-прокси примерно в 180 раз дешевле на один успешный запрос при работе с защищёнными сайтами. Цены провайдеров в 2026 году: Smartproxy — от $7/GB, Bright Data — $8.40/GB, Oxylabs — $8/GB.
Добавить прокси в Playwright можно так:
browser = await pw.chromium.launch(
proxy={"server": "http://proxy-host:port",
"username": "user", "password": "pass"}
)
Обработка всплывающего окна согласия с cookie
Пользователи постоянно жалуются на окно «I agree to terms» как на блокирующий фактор: «сначала Google покажет popup с согласием на условия». Самый чистый способ — заранее установить cookie SOCS (как показано в шаге 3). Если это не сработает, просто нажми кнопку вручную:
try:
accept_btn = page.locator('button:has-text("Accept all")')
if await accept_btn.is_visible(timeout=3000):
await accept_btn.click()
await page.wait_for_timeout(1000)
except Exception:
pass # Всплывающего окна нет
Примечание: текст кнопки зависит от локали — в немецкой версии это «Alle akzeptieren», во французской — «Tout accepter».
Краткая шпаргалка по антибот-методам
| Метод | Сложность | Эффективность | Нужен код? |
|---|---|---|---|
| Случайные задержки (2–7 сек) | Низкая | Средняя | 2 строки |
| Ротация user-agent | Низкая | Средняя | 5 строк |
| Обход headless-детекта | Средняя | Высокая | launch args Playwright |
| Плагин playwright-stealth | Средняя | 60–80% на базовых сайтах | pip install |
| Ротация прокси (дата-центр) | Средняя | Средняя | Конфиг |
| Ротация прокси (residential) | Средняя | 85–95% успеха | Конфиг |
| Предустановка cookie согласия (SOCS) | Низкая | Обязательно | 1 строка |
Рекомендованная безопасная частота: держи паузу 10–20 секунд между запросами при ротации IP. Порог Google примерно 100 запросов в минуту на IP до получения 429, а при устойчивой нагрузке свыше 1,000 запросов в день на IP могут включиться временные блокировки.
Почему ваши селекторы Google Flights постоянно ломаются и как это исправить
Это, безусловно, главная боль. На форумах полно сообщений в духе «в ответ я получаю только 14 пустых списков». Каждый туториал даёт селекторы. Но почти никто не объясняет, почему они ломаются.
Почему селекторы Google Flights меняются
Причин три:
-
Обфускация через Closure Compiler. Google использует Closure Stylesheets для генерации классов вроде
BVAVmfиYMlIzчерезgoog.setCssNameMapping(). Они меняются при каждом билде — иногда еженедельно. -
A/B-тестирование. Разным пользователям одновременно показываются разные HTML-структуры. Твой парсер может работать у тебя и падать у человека в другом регионе.
-
Различия локалей. Пользователи из ЕС видят другие термины, другой layout и даже другие поля данных, чем пользователи из США.
Пиши устойчивые селекторы
Предпочитай селекторы, привязанные к смыслу, а не к внешнему виду:
# Хрупкий вариант — ломается при каждом билде
price_el = await card.query_selector("div.BVAVmf > div.YMlIz")
# Более устойчивый — завязан на accessibility labels
dep_el = await card.query_selector('span[aria-label*="Departure time"]')
# Тоже устойчивый — поиск по тексту
more_btn = page.locator('button:has-text("Show more flights")')
Иерархия устойчивости селекторов (от самых надёжных к самым слабым):
- Атрибуты
aria-label— связаны с доступностью, меняются редко - Атрибуты
data-*— добавляются специально для функциональности - Атрибуты
role— семантические роли ARIA - Текстовые селекторы — по видимому контенту
- Поиск по части class — например,
[class*="price"] - Полные обфусцированные имена классов — по возможности избегайте
Добавьте функцию валидации
Не позволяй сломанным селекторам тихо возвращать пустые данные. Лучше поймать проблему сразу:
import logging
logger = logging.getLogger(__name__)
def validate_flight(data: FlightData) -> bool:
required = ["airline", "price", "departure_time",
"arrival_time", "duration"]
valid = True
for field_name in required:
if not getattr(data, field_name, ""):
logger.warning(
f"Не найдено значение '{field_name}' — возможно, селекторы нужно обновить"
)
valid = False
return valid
Проверяй этим каждый результат. Если начали появляться предупреждения, пора открыть страницу и обновить селекторы.
Стратегия поддержки селекторов
- Проверяй селекторы ежемесячно или сразу, если качество данных падает
- Храни селекторы отдельно, в конфиге, чтобы их было легко менять
- Селекторы в этой статье последний раз проверялись: апрель 2026
- В качестве альтернативы рассмотрите библиотеку fast-flights — она использует декодирование Protobuf вместо CSS-селекторов, полностью обходя эту проблему (хотя у неё тоже есть уязвимость, если Google изменит внутренний формат данных)
От одноразового парсинга к автоматическому трекеру цен Google Flights
Большинство туториалов заканчиваются на «сохранить в JSON». Но в заголовке этой статьи обещаны «уведомления о ценах». Пора выполнить обещание.
![]()
Запусти парсер по расписанию
Вариант 1: библиотека Python schedule (самый простой, кроссплатформенный):
import schedule
import time
def run_scraper():
asyncio.run(main())
schedule.every().day.at("06:00").do(run_scraper)
schedule.every().day.at("18:00").do(run_scraper)
while True:
schedule.run_pending()
time.sleep(60)
Вариант 2: cron job (Linux/Mac):
# Запуск ежедневно в 6:00 и 18:00
0 6,18 * * * cd /path/to/scraper && python scraper.py
Вариант 3: Планировщик задач Windows — создай простую задачу, которая будет запускать python scraper.py по нужному расписанию.
Компромисс здесь такой: все эти варианты требуют компьютера, который постоянно включён. Если ты запускаешь это на ноутбуке, который засыпает, часть запусков будет пропущена.
Храни историю цен
Вместо перезаписи JSON-файла начни добавлять данные в SQLite-базу:
import sqlite3
from datetime import datetime
def init_db():
conn = sqlite3.connect("flights.db")
conn.execute("""
CREATE TABLE IF NOT EXISTS flights (
id INTEGER PRIMARY KEY AUTOINCREMENT,
scrape_date TEXT NOT NULL,
route TEXT NOT NULL,
airline TEXT,
departure_time TEXT,
arrival_time TEXT,
duration TEXT,
stops TEXT,
price_usd REAL,
co2_emissions TEXT
)
""")
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_route_date "
"ON flights(route, scrape_date)"
)
conn.commit()
return conn
def save_flight(conn, route: str, flight: FlightData):
price_num = float(
flight.price.replace("$", "").replace(",", "")
) if flight.price else None
conn.execute(
"INSERT INTO flights "
"(scrape_date, route, airline, departure_time, "
"arrival_time, duration, stops, price_usd, co2_emissions) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
(datetime.now().isoformat(), route, flight.airline,
flight.departure_time, flight.arrival_time,
flight.duration, flight.stops, price_num,
flight.co2_emissions)
)
conn.commit()
Через неделю парсинга дважды в день у тебя уже будет достаточно данных, чтобы заметить тренды.
Анализируй динамику цен и настраивай оповещения
Найди самый дешёвый вариант в исторических данных:
import pandas as pd
import sqlite3
conn = sqlite3.connect("flights.db")
df = pd.read_sql_query(
"SELECT * FROM flights WHERE route = 'SFO-JFK'", conn
)
summary = df.groupby("scrape_date")["price_usd"].agg(
["min", "max", "mean"]
)
cheapest = df.loc[df["price_usd"].idxmin()]
print(
f"Самый дешёвый вариант: ${cheapest['price_usd']:.0f} на "
f"{cheapest['scrape_date']} ({cheapest['airline']})"
)
Отправляй email-уведомление, когда цена опускается ниже заданного порога:
import smtplib
from email.mime.text import MIMEText
def send_price_alert(route, price, threshold, recipient):
msg = MIMEText(
f"Цена упала! {route}: ${price:.0f} "
f"(ниже вашего порога ${threshold:.0f})"
)
msg["Subject"] = f"Предложение на рейс: {route} за ${price:.0f}"
msg["From"] = "alerts@example.com"
msg["To"] = recipient
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
server.login("alerts@example.com", "your_app_password")
server.send_message(msg)
# После каждого парсинга проверяем наличие выгодных цен
min_price = df["price_usd"].min()
threshold = 250
if min_price < threshold:
send_price_alert("SFO-JFK", min_price, threshold,
"you@email.com")
Рекомендуемая частота парсинга: два раза в день достаточно для личного отслеживания цен, и это снижает риск детекта. Для бизнес-мониторинга — каждые 4–6 часов с ротацией прокси. Каждый час — только в период краткосрочных распродаж и лишь на короткое время.
Простой путь: Scheduled Scraper от Thunderbit
Если управлять cron-задачами, постоянно включённым сервером и прокси-конфигурациями кажется тебе слишком большой инфраструктурой, Scheduled Scraper в Thunderbit решает ту же задачу без всей этой нагрузки. Ты описываешь интервал парсинга простыми словами, указываешь URL Google Flights, и скриптер запускается автоматически на облачной инфраструктуре Thunderbit — с уже встроенной антибот-обработкой и экспортом результатов прямо в Google Sheets, Excel или Airtable. Это не замена полноценному Python-подходу (ты теряешь гибкость), но для задачи «нужна таблица для отслеживания цен» это самый быстрый путь. Попробовать можно на бесплатном тарифе.
Когда Python — это избыточно: no-code способы парсить Google Flights
После того как ты всё это соберёшь, скажу честно: компонентов много. Не всем нужен такой уровень контроля. Селекторы ломаются, прокси нужно ротировать, cron-задачи надо мониторить. Если твоя цель — просто регулярно получать цены на билеты в таблицу, есть более быстрые варианты.
Сравнение: Python своими руками vs. API-сервисы vs. Thunderbit
| Подход | Время настройки | Нужен код | Обработка антибот-защиты | Расписание | Стоимость |
|---|---|---|---|---|---|
| DIY Playwright (этот туториал) | 1–2 часа | Python (средний уровень) | Вручную | Вручную (cron) | Бесплатно + стоимость прокси |
| Серверный endpoint SerpApi для Google Flights | 15 мин | Только API-запросы | Уже решено | Через API | От ~$50/мес |
| Thunderbit Chrome Extension | 2 мин | Не нужен | Облачный парсинг | Встроенный планировщик | Есть бесплатный тариф |
Небольшое замечание про SerpApi: в декабре 2025 года Google подал против SerpApi иск по DMCA, утверждая, что объём их запросов вырос на 25 000% за два года. Если ты выбираешь API-провайдера, эту правовую неопределённость стоит учитывать.
Как Thunderbit парсит Google Flights
Открой результаты поиска Google Flights в Chrome, нажми кнопку Thunderbit «AI Suggest Fields» — ИИ прочитает страницу и предложит колонки вроде авиакомпании, цены, времени вылета и пересадок — проверь предложенные поля и нажми «Scrape». Результаты появятся в таблице, которую можно экспортировать в Excel, Google Sheets, Airtable или Notion — всё это доступно на бесплатном тарифе.
Для задачи отслеживания цен особенно полезны Scheduled Scraper и Cloud Scraping от Thunderbit, который умеет обрабатывать до 50 страниц одновременно. Они полностью заменяют всю инфраструктуру из cron, прокси и серверов.
Python даёт полный контроль и практически безграничную кастомизацию. Thunderbit даёт скорость и нулевое обслуживание. Выбирай исходя из своей реальной задачи. Если хочешь узнать больше о no-code-подходах, посмотри наш гид по лучшим no-code web scraper’ам.

Законно ли парсить Google Flights? Что важно знать
Пользователи форумов часто задают этот вопрос: «прямой парсинг Google Flights нарушает условия использования Google». Опасение понятное — особенно с учётом того, что API больше нет и легальной альтернативы от Google тоже нет.
Нарушение ToS vs. юридическая ответственность
В условиях использования Google (обновлены 22 мая 2024 года) сказано, что пользователи не должны «получать доступ или использовать сервисы или любой контент с помощью автоматизированных средств (таких как роботы, пауки или скрейперы)». Нарушение ToS — это нарушение договора, то есть гражданско-правовой вопрос; это не то же самое, что нарушение закона.
Ключевой прецедент: hiQ v. LinkedIn (9-й окружной суд, 2022) установил, что парсинг общедоступных данных не нарушает Computer Fraud and Abuse Act (CFAA). Однако дело завершилось мировым соглашением, а декабрьский иск Google 2025 года против SerpApi опирается на другую правовую теорию — DMCA Section 1201 (обход технических средств защиты), — что может быть серьёзнее.
Лучшие практики ответственного парсинга
- Ограничивай частоту запросов — задержки 10–20 секунд с ротацией IP
- Не парси персональные данные — цены на билеты являются публично отображаемыми агрегированными данными
- Не обходи CAPTCHA программно (именно здесь возникает риск по DMCA)
- Используй данные для личного анализа, а не для создания конкурирующего коммерческого продукта без соответствующей лицензии
- Рассмотри официальные API, если они доступны
Альтернативные источники данных
Если для твоего сценария парсинг кажется слишком рискованным, есть легальные API-варианты:
| Поставщик | Стоимость | Бесплатный тариф | Примечания |
|---|---|---|---|
| SerpApi | $75–$3,750+/мес | 250 поисков/мес | JSON по Google Flights напрямую (под юридическим вниманием) |
| Kiwi Tequila | Бесплатно (affiliate-модель) | Безлимитно | Лучший вариант для стартапов и тестов |
| Amadeus | Оплата по факту | 2,000 запросов/мес | Более 400 авиакомпаний, есть бронирование |
| Skyscanner | Индивидуально | Требуется одобрение | 52 рынка, 30 языков |
Если хочешь полный контекст, у нас есть более подробный разбор правовых аспектов web scraping.
Итоги и главные выводы
Информации было много. Вот что действительно важно:
- Python + Playwright — самый гибкий способ парсить Google Flights, но он требует постоянной поддержки
- Антибот-меры — задержки, ротация user-agent, residential-прокси — не опция, а необходимость, особенно после SearchGuard
- Селекторы часто ломаются — по возможности используй
aria-labelи текстовые селекторы, проверяй результат и планируй регулярное обслуживание - Автоматизируй через
scheduleили cron, чтобы превратить одноразовый парсинг в полноценный трекер цен с историей и email-уведомлениями - Thunderbit предлагает no-code-альтернативу со встроенным планировщиком, облачным парсингом и обработкой антибот-защиты — идеально, если тебе нужна таблица для отслеживания цен, а не полноценный кодовый проект
- Соблюдай правовые границы — ограничивай частоту запросов, собирай только публичные данные и рассматривай API-альтернативы для коммерческого использования
Берите код из этого руководства или установи расширение Thunderbit для Chrome, если хочешь пройти путь быстрее. В любом случае ты начнёшь отслеживать цены на авиабилеты вместо того, чтобы вручную обновлять Google Flights.
Больше техник Python-парсинга ты найдёшь в наших гайдах о том, как парсить сайты на Python и о лучших инструментах для web scraping на Python.
FAQ
1. Можно ли парсить Google Flights без Python?
Да. API-сервисы вроде SerpApi и Kiwi Tequila предоставляют структурированные данные о перелётах через API-запросы — без автоматизации браузера. А если нужен полностью no-code-вариант, расширение Thunderbit для Chrome может напрямую парсить результаты Google Flights из браузера с полями, предложенными ИИ, и экспортом в один клик.
2. Блокирует ли Google парсинг авиарейсов?
Google использует детектирование ботов (SearchGuard), CAPTCHA и rate limiting. При правильных антибот-меры — случайных задержках, ротации user-agent, residential-прокси и stealth-настройках браузера — можно стабильно работать на умеренных объёмах. Конкретные техники и пороги смотри в разделе про антибот-защиту выше.
3. Как часто нужно парсить Google Flights для отслеживания цен?
Двух запусков в день в случайное время достаточно для личного мониторинга цен, и это снижает риск детекта. Для бизнес-мониторинга — каждые 4–6 часов с ротацией прокси. Избегай почасового парсинга, кроме краткосрочных распродаж, — это заметно повышает риск блокировки.
4. Есть ли бесплатный Google Flights API?
Официальный Google QPX Express API был отключён в апреле 2018 года. Бесплатной официальной замены нет. Ближе всего к бесплатному варианту — Kiwi Tequila API (affiliate-модель, безлимитные запросы). SerpApi даёт 250 бесплатных поисков в месяц. Для большинства пользователей практичнее либо парсинг, либо no-code-инструмент вроде Thunderbit.
5. Почему мои CSS-селекторы Google Flights постоянно возвращают пустые данные?
Google использует Closure Compiler для генерации обфусцированных имён классов, которые меняются при каждом билде. A/B-тесты и различия локалей тоже приводят к тому, что HTML-структура отличается у разных пользователей. Решение: используй aria-label и текстовые селекторы вместо имён классов, добавь функцию валидации, чтобы быстро ловить поломки, и проверяй селекторы каждый месяц. Подробную стратегию см. в разделе про поддержку селекторов.
Подробнее


