Форма автостраховки чаще всего падает не из-за логики скрипта, а из-за двух вещей: reCAPTCHA v2 на шаге «Получить тариф» и разрыва многошаговой сессии при смене IP между этапами. Первую проблему решает CaptchaAI, вторую — правильно настроенная sticky-сессия прокси. Ниже — рабочая связка для QA-команд, которые тестируют страховые агрегаторы и сайты отдельных страховщиков.
Почему рвётся многошаговая сессия при смене IP
Расчёт страхового тарифа почти никогда не укладывается в один запрос — это всегда цепочка из нескольких шагов, и сервер страховщика привязывает всю сессию к IP-адресу первого запроса. Смена IP посередине потока не просто прерывает соединение — она обнуляет уже введённые данные, и форма откатывается к первому шагу:
- Личные данные (имя, дата рождения, индекс) — тот же IP
- Автомобиль/объект (год, марка, модель) — тот же IP
- Выбор покрытия (лимиты, франшиза) — тот же IP
- Решение CAPTCHA (токен) — тот же IP
- Показ котировки — тот же IP
Держите sticky-прокси-сессию с TTL 10–15 минут на весь страховой поток — это дешевле, чем потом разбирать, почему форма сбрасывалась на четвёртом шаге из десяти прогонов.
Для QA-команд из Восточной Европы и СНГ, которые тестируют интеграции американских страховых платформ по аутсорс-контракту, типичная ошибка — гонять весь прогон через один дата-центровый IP. Антибот-система страховщика быстро помечает такой трафик как подозрительный, и CAPTCHA начинает всплывать на каждом шаге вместо одного раза за сессию — резидентный прокси с привязкой к сессии снимает эту проблему.
Где именно страховщики ставят CAPTCHA на пути к котировке
Тип CAPTCHA и момент её появления зависят от вертикали:
| Тип страховки | CAPTCHA | Точка срабатывания | Сложность котировки |
|---|---|---|---|
| Автострахование | reCAPTCHA v2 | Отправка формы котировки | Многоэтапная (автомобиль + водитель) |
| Медицинское страхование | reCAPTCHA v2 | Сравнение планов | Многоэтапная (демографические данные) |
| Страхование жилья | CAPTCHA-изображение | Поиск адреса | Средняя |
| Страхование жизни | reCAPTCHA v2 | Калькулятор тарифа | Простая форма |
| Страхование путешествий | reCAPTCHA v2 Invisible | Запрос цены | Простая форма |
| Страхование бизнеса | reCAPTCHA v3 | Контактная форма/котировка | Длинная форма |
Скрипт сбора котировок у нескольких страховщиков
Ниже — рабочий сборщик на Python: он проходит форму по шагам, решает reCAPTCHA v2 через CaptchaAI и сортирует полученные тарифы по цене.
import requests
import time
import re
import base64
CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"
def solve_recaptcha(sitekey, pageurl):
resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
"key": CAPTCHAAI_KEY, "method": "userrecaptcha",
"googlekey": sitekey, "pageurl": pageurl, "json": 1,
})
task_id = resp.json()["request"]
for _ in range(60):
time.sleep(5)
result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
"key": CAPTCHAAI_KEY, "action": "get",
"id": task_id, "json": 1,
})
data = result.json()
if data["request"] != "CAPCHA_NOT_READY":
return data["request"]
raise TimeoutError("Timeout")
def solve_image_captcha(image_bytes):
img_b64 = base64.b64encode(image_bytes).decode()
resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
"key": CAPTCHAAI_KEY, "method": "base64",
"body": img_b64, "json": 1,
})
task_id = resp.json()["request"]
for _ in range(20):
time.sleep(3)
result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
"key": CAPTCHAAI_KEY, "action": "get",
"id": task_id, "json": 1,
})
data = result.json()
if data["request"] != "CAPCHA_NOT_READY":
return data["request"]
raise TimeoutError("Timeout")
class InsuranceQuoteCollector:
def __init__(self, proxy=None):
self.session = requests.Session()
if proxy:
self.session.proxies = {"http": proxy, "https": proxy}
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
})
def get_auto_quote(self, carrier_url, vehicle_data, driver_data, sitekey):
"""Get auto insurance quote from a single carrier."""
# Step 1: Load quote page
self.session.get(carrier_url)
# Step 2: Submit vehicle info
resp = self.session.post(f"{carrier_url}/vehicle", data=vehicle_data)
# Step 3: Submit driver info
resp = self.session.post(f"{carrier_url}/driver", data=driver_data)
# Step 4: Solve CAPTCHA on quote page
token = solve_recaptcha(sitekey, f"{carrier_url}/quote")
# Step 5: Get quote
resp = self.session.post(f"{carrier_url}/quote", data={
"g-recaptcha-response": token,
})
if resp.status_code == 200:
return self._parse_quote(resp.text)
return None
def compare_carriers(self, carriers, vehicle_data, driver_data):
"""Compare quotes across multiple carriers."""
quotes = []
for carrier in carriers:
try:
quote = self.get_auto_quote(
carrier_url=carrier["url"],
vehicle_data=vehicle_data,
driver_data=driver_data,
sitekey=carrier["sitekey"],
)
quotes.append({
"carrier": carrier["name"],
"status": "success",
"quote": quote,
})
except Exception as e:
quotes.append({
"carrier": carrier["name"],
"status": "failed",
"error": str(e),
})
time.sleep(5) # Delay between carriers
# Sort by price
successful = [q for q in quotes if q["status"] == "success" and q["quote"]]
successful.sort(key=lambda x: x["quote"].get("monthly_premium", float("inf")))
return {
"quotes": quotes,
"best_rate": successful[0] if successful else None,
"total_compared": len(carriers),
"successful": len(successful),
}
def _parse_quote(self, html):
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
premium_el = soup.select_one(".premium, .monthly-rate, .quote-amount")
coverage_el = soup.select_one(".coverage-summary, .plan-details")
return {
"monthly_premium": premium_el.get_text(strip=True) if premium_el else "",
"coverage": coverage_el.get_text(strip=True) if coverage_el else "",
}
# Usage
collector = InsuranceQuoteCollector(
proxy="http://user-session-abc:[email protected]:5000"
)
vehicle = {
"year": "2022",
"make": "Toyota",
"model": "Camry",
"vin": "",
"mileage": "15000",
}
driver = {
"age": "35",
"gender": "M",
"zip": "90210",
"driving_record": "clean",
}
carriers = [
{"name": "Carrier A", "url": "https://carrier-a.example.com/auto", "sitekey": "6Lc_xxx1"},
{"name": "Carrier B", "url": "https://carrier-b.example.com/auto", "sitekey": "6Lc_xxx2"},
{"name": "Carrier C", "url": "https://carrier-c.example.com/auto", "sitekey": "6Lc_xxx3"},
]
comparison = collector.compare_carriers(carriers, vehicle, driver)
print(f"Best rate: {comparison['best_rate']}")
Сравнение тарифов на медицинском маркетплейсе
Логика для медицинских маркетплейсов та же самая — меняется только состав данных: вместо марки и модели автомобиля форма запрашивает демографические данные заявителя.
def compare_health_plans(marketplace_url, demographics, sitekey):
"""Compare health insurance plans on marketplace sites."""
collector = InsuranceQuoteCollector(
proxy="http://user-session-xyz:[email protected]:5000"
)
# Load marketplace
collector.session.get(marketplace_url)
# Submit demographics
collector.session.post(f"{marketplace_url}/demographics", data=demographics)
# Solve CAPTCHA for plan results
token = solve_recaptcha(sitekey, f"{marketplace_url}/plans")
resp = collector.session.post(f"{marketplace_url}/plans", data={
"g-recaptcha-response": token,
})
if resp.status_code == 200:
from bs4 import BeautifulSoup
soup = BeautifulSoup(resp.text, "html.parser")
plans = []
for card in soup.select(".plan-card, .insurance-plan"):
plans.append({
"name": card.select_one(".plan-name").get_text(strip=True) if card.select_one(".plan-name") else "",
"premium": card.select_one(".premium").get_text(strip=True) if card.select_one(".premium") else "",
"deductible": card.select_one(".deductible").get_text(strip=True) if card.select_one(".deductible") else "",
})
return plans
return []
Типичные сбои и их устранение
| Проблема | Причина | Решение |
|---|---|---|
| Форма котировки сбрасывается на середине потока | IP менялся между шагами | Использовать sticky-прокси-сессию |
| «Невозможно предоставить котировку» | Не прошла валидация данных | Проверить обязательные поля |
| CAPTCHA появляется повторно | Первый токен истёк до отправки | Решить CAPTCHA заново сразу же |
| Тариф отличается от расчёта вручную | Разошлись cookies или сессия | Начать чистую сессию |
| Rate limiter блокирует запросы | Слишком много котировок с одного IP | Снизить темп, менять IP между страховщиками |
Если тестовый прогон логирует персональные данные заявителя — ФИО, дату рождения, адрес, — собирайте только то, на обработку чего у вас есть основание. Для команд, работающих с данными в РФ, это требование 152-ФЗ «О персональных данных»; для трансграничных проектов уместна аналогичная дисциплина в духе GDPR.
Чек-лист перед первым прогоном
Прежде чем гонять полный список страховщиков, проверьте эти пять пунктов — это экономит часы отладки на типичных кейсах из раздела FAQ ниже:
- sitekey и pageurl сняты с реальной отрисовки виджета, а не с адреса iframe
- sticky-прокси-сессия держит один IP на весь поток, TTL ≥ 10 минут
- обязательные поля формы (индекс, дата рождения, VIN) прошли локальную валидацию до отправки
- лог прогона не хранит персональные данные заявителя дольше, чем нужно для проверки
- частота запросов на одного страховщика не превышает 20–50 котировок в сутки
Часто задаваемые вопросы
Как извлечь sitekey и pageurl, если форма котировки открывается в iframe или подгружается через AJAX?
Откройте DevTools → Network и найдите запрос к домену Google (recaptcha/api2/anchor или api.js) — параметр k в его URL и есть sitekey. В качестве pageurl берите адрес страницы, где виджет реально отрисован, а не адрес iframe.
Что делать, если после решения reCAPTCHA страховщик всё равно отклоняет заявку с ошибкой валидации?
Почти всегда дело не в CAPTCHA, а в несовпадении данных формы — проверьте формат индекса, даты рождения и VIN. Решённый токен CAPTCHA не спасает от отклонения по некорректным данным.
Почему сессия обрывается, если IP меняется между вводом данных и решением CAPTCHA?
Сервер страховщика привязывает весь многошаговый поток к IP-адресу первого запроса — это защита от подмены данных между шагами. Смена IP посередине читается сервером как новая сессия, и уже введённые данные теряются.
Сколько котировок в день можно безопасно собирать с одного IP?
На практике 20–50 котировок на одного страховщика в сутки с паузами между запросами. Выше этого порога чаще срабатывают rate limiter и CAPTCHA начинает появляться на каждом шаге, а не один раз за сессию.
Какие данные из формы котировки стоит логировать при тестировании?
Логируйте только то, что нужно для проверки самого потока: код ответа, факт решения CAPTCHA, итоговый тариф. Персональные данные тестового заявителя (ФИО, дату рождения, адрес) лучше не хранить дольше, чем требует сама проверка.
Что читать дальше
- Закреплённые и ротируемые сессии прокси
- Сохранение сессии браузера в workflow с CAPTCHA
- Способы аутентификации прокси в CaptchaAI
Автоматизируйте сравнение котировок у нескольких страховщиков — получите API-ключ CaptchaAI и подключите решение reCAPTCHA к своему пайплайну.