Direktori penyedia layanan kesehatan, portal harga obat, dan registrasi uji klinis yang bersifat publik umumnya boleh dikumpulkan untuk riset dan analitik — CAPTCHA di baliknya adalah mekanisme anti-bot, bukan larangan hukum. Tantangannya murni teknis: reCAPTCHA v2 mendominasi portal modern, image CAPTCHA masih bertahan di direktori lama, dan sesekali Cloudflare Turnstile muncul di halaman penilaian rumah sakit.
Artikel ini memberi Anda dua scraper Python siap pakai — direktori penyedia bergaya NPI dan registrasi uji klinis — plus tabel kepatuhan data yang wajib dicek sebelum menjalankan keduanya dalam skala besar.
Di Mana CAPTCHA Menghambat Data Kesehatan
| Sumber Data | Jenis CAPTCHA | Data yang Diambil | Kegunaan |
|---|---|---|---|
| Direktori penyedia (NPI) | Image CAPTCHA | Pencarian dokter/fasilitas | Kecukupan jaringan |
| Portal harga obat | reCAPTCHA v2 | Harga obat | Transparansi harga |
| Registrasi uji klinis | reCAPTCHA v2 | Data dan hasil uji coba | Analisis riset |
| Formularium asuransi | reCAPTCHA v2 | Daftar cakupan obat | Perbandingan formularium |
| Badan perizinan negara bagian | Image CAPTCHA | Verifikasi lisensi | Pemeriksaan kredensial |
| Penilaian kualitas rumah sakit | Cloudflare Turnstile | Metrik kualitas | Analisis kinerja |
Pola yang sama berlaku untuk direktori fasilitas kesehatan publik di pasar lain — misalnya direktori faskes ala BPJS Kesehatan di Indonesia atau layanan pembanding harga obat ala GoodRx di AS — karena keduanya menghadapi tekanan bot scraping yang sama dan memasang CAPTCHA sebagai pengaman lini pertama.
Kepatuhan Data Sebelum Mulai Scraping
| Jenis Data | Sensitivitas | Rekomendasi |
|---|---|---|
| Direktori penyedia | Rendah (info publik) | Umumnya aman dikumpulkan |
| Harga obat | Rendah (harga publik) | Diizinkan untuk transparansi |
| Metadata uji klinis | Rendah (registrasi publik) | Sesuai untuk penggunaan riset |
| Ulasan pasien | Sedang | Anonimkan sebelum dianalisis |
| Detail paket asuransi | Rendah (tarif dipublikasikan) | Diizinkan untuk perbandingan |
Penting: jangan pernah mengumpulkan protected health information (PHI) — data yang mengidentifikasi pasien individu, seperti nomor rekam medis atau riwayat perawatan personal. Fokus hanya pada data publik yang tidak spesifik pasien.
Di Indonesia, UU Pelindungan Data Pribadi (UU 27/2022) mengatur pemrosesan data pribadi, bukan data agregat yang sudah dipublikasikan resmi oleh lembaga seperti direktori faskes atau portal transparansi harga. Prinsipnya sejalan dengan CMS Price Transparency Rule di AS yang justru mendorong keterbukaan harga obat: kumpulkan data yang memang sudah publik, dan hindari apa pun yang menyentuh identitas pasien individu. Ini bukan nasihat hukum — untuk kasus spesifik, cek dengan tim legal Anda.
Bangun Scraper Direktori Penyedia dengan Python
Kelas HealthcareDataCollector di bawah menangani dua pola sekaligus dalam satu alur: reCAPTCHA v2 untuk portal modern (search_providers, lookup_drug_prices) dan image CAPTCHA sebagai fallback otomatis ketika sitekey tidak tersedia — kondisi umum di direktori legacy yang belum migrasi. batch_provider_lookup melakukan loop kombinasi specialty x location lalu langsung mengekspor hasilnya ke CSV.
import requests
import time
import re
import base64
from bs4 import BeautifulSoup
import csv
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 HealthcareDataCollector:
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 search_providers(self, portal_url, specialty, location, sitekey=None):
"""Search provider directory with CAPTCHA handling."""
resp = self.session.get(portal_url, timeout=30)
data = {"specialty": specialty, "location": location}
# Handle CAPTCHA
if sitekey:
token = solve_recaptcha(sitekey, portal_url)
data["g-recaptcha-response"] = token
else:
captcha_img = re.search(r'src="(/captcha[^"]+)"', resp.text)
if captcha_img:
img_url = portal_url.rstrip("/") + captcha_img.group(1)
img = self.session.get(img_url)
data["captcha"] = solve_image_captcha(img.content)
resp = self.session.post(portal_url, data=data)
return self._parse_providers(resp.text)
def lookup_drug_prices(self, pricing_url, drug_name, zip_code, sitekey):
"""Look up drug prices with CAPTCHA solving."""
# Load search page
self.session.get(pricing_url)
# Solve CAPTCHA
token = solve_recaptcha(sitekey, pricing_url)
resp = self.session.post(pricing_url, data={
"drug": drug_name,
"zip": zip_code,
"g-recaptcha-response": token,
})
if resp.status_code == 200:
return self._parse_prices(resp.text)
return []
def batch_provider_lookup(self, portal_url, specialties, locations, output_file):
"""Batch search across specialties and locations."""
all_providers = []
for specialty in specialties:
for location in locations:
try:
providers = self.search_providers(
portal_url, specialty, location,
)
for p in providers:
p["specialty_search"] = specialty
p["location_search"] = location
all_providers.extend(providers)
print(f"{specialty} / {location}: {len(providers)} providers")
time.sleep(5)
except Exception as e:
print(f"Error: {specialty} / {location}: {e}")
# Export
if all_providers:
keys = all_providers[0].keys()
with open(output_file, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=keys)
writer.writeheader()
writer.writerows(all_providers)
return all_providers
def _parse_providers(self, html):
soup = BeautifulSoup(html, "html.parser")
providers = []
for card in soup.select(".provider-card, .doctor-result, tr.provider"):
providers.append({
"name": self._text(card, ".name, .provider-name"),
"specialty": self._text(card, ".specialty"),
"address": self._text(card, ".address"),
"phone": self._text(card, ".phone"),
"accepting": self._text(card, ".accepting-patients"),
})
return providers
def _parse_prices(self, html):
soup = BeautifulSoup(html, "html.parser")
prices = []
for row in soup.select(".pharmacy-row, .price-result"):
prices.append({
"pharmacy": self._text(row, ".pharmacy-name"),
"price": self._text(row, ".price, .drug-price"),
"quantity": self._text(row, ".quantity"),
})
return prices
def _text(self, el, selector):
found = el.select_one(selector)
return found.get_text(strip=True) if found else ""
# Usage
collector = HealthcareDataCollector(
proxy="http://user:pass@residential.proxy.com:5000"
)
# Provider search
providers = collector.search_providers(
portal_url="https://provider-directory.example.com/search",
specialty="Cardiology",
location="New York, NY",
)
# Drug pricing
prices = collector.lookup_drug_prices(
pricing_url="https://drug-prices.example.com/compare",
drug_name="atorvastatin",
zip_code="10001",
sitekey="6Lc_xxxxxxx",
)
Kumpulkan Data Registrasi Uji Klinis
Alur untuk registrasi uji klinis lebih sederhana karena hampir selalu memakai reCAPTCHA v2 saja, tanpa fallback image CAPTCHA: kirim kondisi medis dan status recruiting, selesaikan reCAPTCHA v2 di search_url, lalu parse daftar studi yang cocok.
def collect_clinical_trials(search_url, condition, sitekey):
"""Collect clinical trial data for a medical condition."""
collector = HealthcareDataCollector(
proxy="http://user:pass@residential.proxy.com:5000"
)
token = solve_recaptcha(sitekey, search_url)
resp = collector.session.post(search_url, data={
"condition": condition,
"status": "recruiting",
"g-recaptcha-response": token,
})
if resp.status_code != 200:
return []
soup = BeautifulSoup(resp.text, "html.parser")
trials = []
for item in soup.select(".trial-item, .study-result"):
trials.append({
"title": collector._text(item, ".title, h3"),
"status": collector._text(item, ".status"),
"sponsor": collector._text(item, ".sponsor"),
"phase": collector._text(item, ".phase"),
"enrollment": collector._text(item, ".enrollment"),
"location": collector._text(item, ".location"),
})
return trials
Mengatasi Kendala Umum saat Scraping Data Kesehatan
| Kendala | Penyebab | Solusi |
|---|---|---|
| Image CAPTCHA tidak terbaca | Kualitas gambar rendah | Coba ulang — gambar baru otomatis di-generate |
| Pencarian penyedia kembali kosong | CAPTCHA memblokir submit | Selesaikan CAPTCHA sebelum mengirim form |
| Harga obat berbeda per lokasi | Harga berbasis geografis | Cocokkan lokasi proxy dengan kode pos |
| Sesi berakhir di tengah pencarian multi-halaman | Timeout portal | Percepat alur pencarian, kurangi jeda antar halaman |
| Rate limit saat batch lookup | Terlalu banyak request beruntun | Tambahkan jeda 5–10 detik antar request |
| Latensi tinggi dari region jauh | Scraper dan portal berada di region berbeda | Deploy dekat target (mis. AWS ap-southeast-1 Singapura atau ap-southeast-3 Jakarta) atau naikkan timeout |
Pertanyaan Umum seputar Scraping Data Healthcare
Berapa lama waktu penyelesaian reCAPTCHA v2 saat scraping direktori penyedia dalam volume besar?
reCAPTCHA v2 biasanya selesai di bawah 60 detik dengan tingkat keberhasilan tinggi pada tipe yang didukung, sementara image CAPTCHA di direktori legacy jauh lebih cepat — di bawah 0.5 detik. Total waktu batch_provider_lookup lebih ditentukan oleh jumlah kombinasi specialty dan location yang Anda loop, bukan oleh solver itu sendiri — beri jeda antar request dan sesuaikan jumlah thread dengan paket CaptchaAI Anda.
Apakah CaptchaAI bisa menangani Cloudflare Turnstile di portal penilaian kualitas rumah sakit?
Ya. Cloudflare Turnstile didukung penuh dan biasanya clear di bawah 10 detik. Kirim sitekey dan page URL ke endpoint yang sama seperti reCAPTCHA v2, cukup ganti method menjadi turnstile sesuai dokumentasi API.
Apakah UU PDP membatasi pengumpulan direktori penyedia dan harga obat publik?
UU Pelindungan Data Pribadi (UU 27/2022) mengatur data pribadi, bukan data agregat yang sudah dipublikasikan resmi. Direktori penyedia, harga obat publik, dan metadata uji klinis umumnya aman dikumpulkan selama Anda tidak menyimpan data pasien individu. Ini bukan nasihat hukum — untuk kasus spesifik, konsultasikan tim legal Anda.
Apa bedanya menangani image CAPTCHA di direktori NPI dengan reCAPTCHA v2 di portal harga obat?
Image CAPTCHA mengharuskan Anda mengunduh gambar lalu mengirim base64-nya ke solve_image_captcha, sedangkan reCAPTCHA v2 cukup mengirim sitekey dan page URL ke solve_recaptcha. Direktori lama yang belum migrasi biasanya masih pakai image CAPTCHA; portal harga obat modern hampir selalu sudah pindah ke reCAPTCHA v2.
Bisakah satu scraper menangani reCAPTCHA v2 dan image CAPTCHA sekaligus?
Bisa — lihat search_providers pada contoh kode: fungsi ini mengecek dulu apakah sitekey tersedia, lalu memilih solve_recaptcha atau solve_image_captcha secara otomatis. Anda tidak perlu scraper terpisah untuk tiap jenis portal.
Panduan Terkait
- Otomasi portal pemerintah
- Scraping riset akademik
- Egress jaringan yang diotorisasi untuk scraping skala besar
Kumpulkan data healthcare secara efisien — dapatkan API key CaptchaAI dan jalankan scraper direktori penyedia serta harga obat tanpa terhambat CAPTCHA.