Saat tim QA menguji agregator asuransi atau alur penawaran miliknya sendiri, hambatan otomatisasi hampir selalu muncul di titik yang sama: form penawaran memunculkan reCAPTCHA tepat sebelum tarif ditampilkan. CaptchaAI menyelesaikan tantangan itu dan mengembalikan token, sehingga skrip pengujian Anda bisa merampungkan form multi-step dari langkah pertama sampai halaman hasil. Artikel ini menunjukkan cara menyusun kolektor penawaran multi-carrier di Python, menjaga session tetap konsisten antar langkah, dan menangani reCAPTCHA v2, reCAPTCHA v3, hingga Image CAPTCHA yang dipakai berbagai operator asuransi.
Di ekosistem insurtech dan agregator lokal yang tumbuh cepat, tim data kecil kerap harus memverifikasi puluhan kombinasi tarif dalam sekali putaran pengujian. Mengerjakannya manual tidak realistis — dan justru di sinilah penanganan CAPTCHA otomatis menjadi bagian wajar dari pipeline QA.
Di mana CAPTCHA muncul pada alur penawaran asuransi
Setiap vertikal asuransi memicu CAPTCHA di titik yang berbeda, dan kompleksitas form-nya juga tidak seragam. Memetakan pola ini lebih dulu membantu Anda menentukan di langkah mana token perlu diminta. Berikut pola yang paling umum ditemui:
| Jenis Asuransi | CAPTCHA | Titik Pemicu | Kompleksitas Form |
|---|---|---|---|
| Asuransi mobil | reCAPTCHA v2 | Submit form penawaran | Multi-step (kendaraan + pengemudi) |
| Asuransi kesehatan | reCAPTCHA v2 | Perbandingan paket | Multi-step (demografi) |
| Asuransi rumah | Image CAPTCHA | Pencarian alamat | Sedang |
| Asuransi jiwa | reCAPTCHA v2 | Kalkulator nilai | Form sederhana |
| Asuransi perjalanan | reCAPTCHA v2 Invisible | Permintaan penawaran | Form sederhana |
| Asuransi bisnis | reCAPTCHA v3 | Hubungi/form penawaran | Form panjang |
Keenam pola di atas — reCAPTCHA v2, v2 Invisible, v3, dan Image CAPTCHA — semuanya termasuk tipe yang didukung CaptchaAI, jadi satu integrasi sudah cukup untuk menutup seluruh vertikal.
Mengumpulkan penawaran dari banyak carrier sekaligus
Inti alur otomatisasi mengikuti pola empat langkah yang konsisten: kirim task ke in.php → simpan task ID → polling ke res.php → pakai token pada form. Kelas InsuranceQuoteCollector berikut membungkus pola itu, menjalankan langkah kendaraan dan pengemudi lewat satu session, lalu menyelesaikan reCAPTCHA sebelum meminta tarif akhir dari setiap carrier.
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:pass@residential.proxy.com: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']}")
Perhatikan time.sleep(5) di antara carrier: jeda ini bukan sekadar sopan-santun, tetapi menahan laju permintaan agar rate limiter tidak langsung memunculkan CAPTCHA yang lebih agresif. solve_recaptcha sendiri sudah melakukan polling hingga 60 kali dengan interval 5 detik sebelum menyerah dengan TimeoutError, jadi Anda tidak perlu memeriksa hasil secara manual.
Membandingkan paket asuransi kesehatan
Vertikal kesehatan memakai pola yang sama, hanya berbeda pada field demografi dan struktur kartu paket yang diparsing. Fungsi berikut memakai ulang InsuranceQuoteCollector, mengirim data demografi, menyelesaikan reCAPTCHA pada halaman hasil, lalu mengumpulkan setiap paket beserta premi dan deductible-nya.
def compare_health_plans(marketplace_url, demographics, sitekey):
"""Compare health insurance plans on marketplace sites."""
collector = InsuranceQuoteCollector(
proxy="http://user-session-xyz:pass@residential.proxy.com: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 []
Karena logika penyelesaian CAPTCHA sudah terpisah dari logika parsing, menambah vertikal baru — rumah, jiwa, atau perjalanan — hanya soal menyesuaikan field form dan selector HTML, bukan menulis ulang integrasi.
Menjaga session konsisten di setiap langkah
Penawaran asuransi hampir selalu multi-step, dan server mengaitkan setiap langkah ke satu session serta satu alamat IP. Jika IP berubah di tengah alur, server menganggapnya sesi baru dan mereset form. Karena itu satu IP yang stabil sepanjang alur (sticky session) menjadi syarat, bukan opsi:
| Langkah | Data yang Dikirim | Persyaratan IP |
|---|---|---|
| 1. Informasi pribadi | Nama, DOB, zip | IP yang sama |
| 2. Kendaraan/properti | Tahun, merek, model | IP yang sama |
| 3. Pemilihan coverage | Limit, deductible | IP yang sama |
| 4. Solve CAPTCHA | Token | IP yang sama |
| 5. Tampilan penawaran | N/A | IP yang sama |
Sebagai patokan praktis, pertahankan IP yang sama dengan TTL sekitar 10–15 menit untuk menutup satu alur penawaran penuh, termasuk waktu polling CAPTCHA.
Mengatasi masalah yang sering muncul
Sebagian besar kegagalan pada alur penawaran berakar pada session atau token yang kedaluwarsa, bukan pada penyelesaian CAPTCHA itu sendiri. Tabel berikut memetakan gejala ke penyebab dan solusinya:
| Masalah | Penyebab | Solusi |
|---|---|---|
| Form penawaran reset di tengah alur | IP berubah antar langkah | Pertahankan sticky session dengan IP tetap |
| "Tidak dapat memberikan penawaran" | Validasi data gagal | Periksa field yang wajib diisi |
| CAPTCHA muncul dua kali | Token pertama sudah kedaluarsa | Selesaikan ulang segera |
| Tarif berbeda dari manual | Perbedaan cookie/session | Mulai dari sesi bersih |
| Rate limiter memblokir request | Terlalu banyak penawaran dari satu IP | Perlambat, rotasi IP antar carrier |
Memilih paket sesuai skala pengujian
CaptchaAI menagih per thread yang berjalan bersamaan, bukan per solve, dan setiap paket memberi solve tanpa batas selama bulan berjalan. Model ini cocok untuk tim yang cost-sensitive: biaya tetap berapa pun jumlah penawaran yang Anda kumpulkan. Untuk pengujian beberapa carrier secara berurutan, paket BASIC ($15/bulan, 5 thread) biasanya cukup. Jika Anda menjalankan banyak carrier paralel dalam satu putaran regresi, ADVANCE ($90/bulan, 50 thread) memberi ruang thread yang jauh lebih lega. Karena tidak ada biaya per solve, menambah vertikal atau memperbanyak carrier tidak menaikkan tagihan Anda.
Pertanyaan umum
CAPTCHA apa saja yang biasanya muncul di form penawaran asuransi?
Paling sering reCAPTCHA v2 pada submit form, reCAPTCHA v3 pada form bisnis yang panjang, reCAPTCHA v2 Invisible pada penawaran cepat, dan Image CAPTCHA pada pencarian alamat. Keempatnya termasuk tipe yang didukung CaptchaAI.
Apakah pendekatan ini berlaku untuk semua vertikal asuransi?
Ya. Logika penanganan CAPTCHA-nya identik untuk asuransi mobil, kesehatan, rumah, dan jiwa — yang berbeda hanya field form dan selector HTML pada halaman hasil.
Mengapa penawaran tidak muncul padahal token sudah valid?
Umumnya karena session sudah putus: IP berganti di tengah alur, atau token diminta terlalu lama sebelum di-submit sehingga kedaluwarsa. Pastikan IP tetap sama sepanjang langkah dan kirim token segera setelah diterima.
Apakah otomatisasi pengumpulan penawaran ini sesuai aturan data di Indonesia?
Jalankan hanya pada alur milik Anda sendiri atau lingkungan pengujian, dan proses hanya data yang memang Anda berwenang mengolahnya — hindari data pribadi pihak lain sesuai UU Pelindungan Data Pribadi (UU 27/2022). Catatan ini bukan nasihat hukum.
Panduan terkait
- Sticky session vs rotating session untuk alur CAPTCHA
- Menjaga persistensi session browser
- Metode autentikasi proxy di CaptchaAI
Ambil kunci API CaptchaAI dan selesaikan reCAPTCHA di form penawaran pertama Anda hari ini.