Мониторинг статуса рейсов требует частых проверок на нескольких порталах авиакомпаний и аэропортов. Эти порталы защищают свои данные в реальном времени с помощью Cloudflare Turnstile, reCAPTCHA и пользовательских CAPTCHA, особенно когда они обнаруживают повторяющиеся автоматические запросы. Вот как обрабатывать CAPTCHA при создании надежных инструментов отслеживания рейсов.
Где появляются CAPTCHA
| Тип портала | КАПЧА | Курок |
|---|---|---|
| Страница статуса рейса авиакомпании | Cloudflare Turnstile | Частые запросы с одного IP |
| Прибытие в аэропорт/departure табло | страница Cloudflare-защиты в staging | Обнаружение ботов |
| Поисковые системы авиабилетов | reCAPTCHA v2/v3 | Отправка формы поиска |
| Проверка статуса бронирования | reCAPTCHA v2 | Прежде чем показывать маршрут |
| Страницы ограничения скорости API | Пользовательская капча | После превышения лимита запросов |
Архитектура мониторинга полетов
import requests
import time
from datetime import datetime
class FlightMonitor:
def __init__(self, api_key):
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})
def check_flight(self, airline_url, flight_number):
"""Check flight status, handling CAPTCHAs if encountered."""
response = self.session.get(
f"{airline_url}/flight-status/{flight_number}"
)
if self._is_captcha_page(response):
response = self._solve_and_retry(response, airline_url)
return self._parse_flight_data(response.text)
def _is_captcha_page(self, response):
return (
response.status_code == 403 or
"cf-turnstile" in response.text or
"g-recaptcha" in response.text
)
def _solve_and_retry(self, response, url):
import re
# Detect CAPTCHA type
if "cf-turnstile" in response.text:
match = re.search(r'data-sitekey="(0x[^"]+)"', response.text)
token = self._solve_turnstile(match.group(1), url)
field = "cf-turnstile-response"
else:
match = re.search(r'data-sitekey="([^"]+)"', response.text)
token = self._solve_recaptcha(match.group(1), url)
field = "g-recaptcha-response"
return self.session.post(url, data={field: token})
def _solve_turnstile(self, site_key, page_url):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": self.api_key,
"method": "turnstile",
"sitekey": site_key,
"pageurl": page_url,
"json": 1
})
task_id = resp.json()["request"]
return self._poll_result(task_id)
def _solve_recaptcha(self, site_key, page_url):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": page_url,
"json": 1
})
task_id = resp.json()["request"]
return self._poll_result(task_id)
def _poll_result(self, task_id):
for _ in range(60):
time.sleep(3)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id,
"json": 1
})
data = result.json()
if data["status"] == 1:
return data["request"]
raise TimeoutError("CAPTCHA solve timed out")
def _parse_flight_data(self, html):
# Parse flight status from HTML
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
def text_or_none(node):
return node.text.strip() if node and node.text else None
return {
"status": text_or_none(soup.select_one(".flight-status")),
"departure": text_or_none(soup.select_one(".departure-time")),
"arrival": text_or_none(soup.select_one(".arrival-time")),
"gate": text_or_none(soup.select_one(".gate-info")),
"checked_at": datetime.now().isoformat()
}
Периодический мониторинг с обработкой CAPTCHA
def monitor_flight(monitor, airline_url, flight_number,
interval_seconds=300, max_checks=48):
"""Monitor a flight every N seconds, handling CAPTCHAs as needed."""
history = []
for check_num in range(max_checks):
try:
status = monitor.check_flight(airline_url, flight_number)
history.append(status)
# Alert on changes
if len(history) > 1 and status["status"] != history[-2]["status"]:
print(f"Status changed: {history[-2]['status']} → {status['status']}")
print(f"Check {check_num + 1}: {status['status']} "
f"(Gate: {status.get('gate', 'Coming soon')})")
except Exception as e:
print(f"Check {check_num + 1} failed: {e}")
time.sleep(interval_seconds)
return history
# Usage
monitor = FlightMonitor("YOUR_API_KEY")
monitor_flight(monitor, "https://airline.example.com", "AA1234")
Мониторинг нескольких авиакомпаний (JavaScript)
class FlightTracker {
constructor(apiKey) {
this.apiKey = apiKey;
this.flights = new Map();
}
async addFlight(airline, flightNumber, checkUrl) {
this.flights.set(flightNumber, {
airline,
url: checkUrl,
history: [],
lastCheck: null
});
}
async checkAll() {
const results = [];
for (const [flightNum, flight] of this.flights) {
try {
const status = await this.checkFlight(flight.url, flightNum);
flight.history.push(status);
flight.lastCheck = new Date();
results.push({ flight: flightNum, ...status });
} catch (error) {
results.push({ flight: flightNum, error: error.message });
}
}
return results;
}
async checkFlight(url, flightNumber) {
const response = await fetch(`${url}/status/${flightNumber}`);
const html = await response.text();
// Check for CAPTCHA
if (html.includes('cf-turnstile') || response.status === 403) {
return this.solveAndRetry(url, flightNumber, html);
}
return this.parseStatus(html);
}
async solveAndRetry(url, flightNumber, html) {
const siteKeyMatch = html.match(/data-sitekey="(0x[^"]+)"/);
if (!siteKeyMatch) throw new Error('No sitekey found');
const token = await this.solveTurnstile(siteKeyMatch[1], url);
const response = await fetch(`${url}/status/${flightNumber}`, {
method: 'POST',
body: new URLSearchParams({ 'cf-turnstile-response': token })
});
return this.parseStatus(await response.text());
}
}
Мониторинг частоты и уровня CAPTCHA
| Проверьте частоту | Типичная скорость CAPTCHA | Рекомендация |
|---|---|---|
| Каждую 1 минуту | Высокий (50–80%) | Слишком агрессивно — увеличьте интервал |
| Каждые 5 минут | Умеренный (10–30%) | Приемлемо для критических полетов |
| Каждые 15 минут | Низкий (5–10%) | Хороший баланс для повседневного мониторинга |
| Каждые 30 минут | Очень низкий (<5%) | подходящий всего для долгосрочного отслеживания |
| Каждый час | Минимальный (<1%) | CAPTCHA срабатывает редко |
Оптимизация сеанса
Уменьшите количество случаев CAPTCHA, сохраняя состояние сеанса:
| Техника | Эффект |
|---|---|
| Сохранять файлы cookie между проверками | Cloudflare qa_validation_cookie действителен в течение 15–30 минут. |
| Используйте последовательный пользовательский агент | Изменение UA порождает новые проблемы |
| Поддерживайте согласованность прокси | Тот же IP снижает подозрения |
| Запросы на пространство равномерно | Пакетные шаблоны запускают ограничения скорости |
Поиск неисправностей
| Проблема | Причина | Исправить |
Следующие шаги
- CaptchaAI Quickstart: ваше первое решение CAPTCHA за 5 минут
- Как решить reCAPTCHA v2 через API: пошаговое руководство
- Как решить Cloudflare Turnstile через API
- Как решить GeeTest v3 с помощью API