Danh mục bác sĩ NPI, giá thuốc và thử nghiệm lâm sàng tại Mỹ đều là dữ liệu công khai — nhưng gần như cổng nào cũng cắm reCAPTCHA v2 hoặc CAPTCHA hình ảnh trước khi cho xem kết quả. Đội outsourcing hay startup health-tech tại Việt Nam dựng dashboard cho khách hàng Mỹ sẽ gặp điểm nghẽn này mỗi khi cổng đổi giao diện. Ba nhóm cổng hay gặp nhất:
- Danh mục nhà cung cấp & hội đồng cấp phép — CAPTCHA hình ảnh
- Giá thuốc & công thức bảo hiểm — reCAPTCHA v2
- Thử nghiệm lâm sàng & xếp hạng bệnh viện — reCAPTCHA v2, Turnstile
CAPTCHA xuất hiện ở những cổng thông tin y tế nào
Sáu nhóm cổng phổ biến nhất:
| Nguồn | Loại CAPTCHA | Dữ liệu | Trường hợp sử dụng |
|---|---|---|---|
| Danh mục nhà cung cấp (NPI) | CAPTCHA hình ảnh | Tra cứu bác sĩ/cơ sở y tế | Đánh giá độ phủ mạng lưới |
| Cổng giá thuốc | reCAPTCHA v2 | Giá thuốc | Minh bạch về giá |
| Sổ đăng ký thử nghiệm lâm sàng | reCAPTCHA v2 | Dữ liệu thử nghiệm, kết quả | Phân tích nghiên cứu |
| Công thức bảo hiểm | reCAPTCHA v2 | Danh sách bảo hiểm thuốc | So sánh gói bảo hiểm |
| Hội đồng cấp phép nhà nước | CAPTCHA hình ảnh | Xác minh giấy phép | Kiểm tra thông tin xác thực |
| Đánh giá chất lượng bệnh viện | Cloudflare Turnstile | Số liệu chất lượng | Phân tích hiệu suất |
Dữ liệu y tế nào được phép thu thập
- Danh mục nhà cung cấp — độ nhạy thấp (công khai), an toàn để thu thập.
- Giá thuốc — độ nhạy thấp (giá công khai), được phép vì mục đích minh bạch.
- Siêu dữ liệu thử nghiệm lâm sàng — độ nhạy thấp (đăng ký công cộng), phù hợp cho nghiên cứu.
- Đánh giá của bệnh nhân — độ nhạy trung bình, cần ẩn danh trước khi phân tích.
- Chi tiết gói bảo hiểm — độ nhạy thấp (tỷ lệ công bố), được phép so sánh.
Đừng bao giờ thu thập thông tin sức khỏe được bảo vệ (PHI); chỉ dùng dữ liệu công khai, không gắn bệnh nhân cụ thể.
Đội tại Việt Nam phục vụ khách hàng Mỹ nên ghi log nguồn dữ liệu theo tinh thần Nghị định 13/2023/NĐ-CP, dù dữ liệu không phải PHI.
Script quét danh mục nhà cung cấp bằng Python
Xử lý cả hai loại CAPTCHA trên cùng một script
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:[email protected]: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",
)
batch_provider_lookup gộp nhiều chuyên khoa và địa điểm rồi xuất thẳng ra CSV.
Thu thập dữ liệu thử nghiệm lâm sàng qua reCAPTCHA v2
Giải token một lần rồi gửi kèm điều kiện tìm kiếm
def collect_clinical_trials(search_url, condition, sitekey):
"""Collect clinical trial data for a medical condition."""
collector = HealthcareDataCollector(
proxy="http://user:[email protected]: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
Xử lý lỗi thường gặp khi thu thập dữ liệu y tế
Lỗi hay gặp khi chạy script ở trên:
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| Hình ảnh CAPTCHA không đọc được | Ảnh chất lượng thấp | Thử lại — ảnh mới sẽ được tạo |
| Tìm kiếm nhà cung cấp trả về rỗng | CAPTCHA đã chặn request | Giải CAPTCHA trước khi gửi form |
| Giá thuốc thay đổi theo địa điểm | Định giá theo vị trí địa lý | Khớp proxy với mã zip đang tra cứu |
| Phiên hết hạn khi lướt nhiều trang | Cổng thông tin timeout | Hoàn thành tìm kiếm nhanh |
| Bị giới hạn tần suất khi tra cứu hàng loạt | Gửi quá nhiều request liên tiếp | Thêm độ trễ 5–10 giây giữa các lần gọi |
Câu hỏi thường gặp về thu thập dữ liệu y tế qua CAPTCHA
Danh mục bác sĩ NPI và giá thuốc có phải dữ liệu được phép thu thập không?
Có. Quy tắc minh bạch giá thuốc của CMS khuyến khích công bố giá; danh mục NPI vốn để tra cứu công khai — miễn không đụng hồ sơ bệnh nhân cá nhân.
CaptchaAI xử lý CAPTCHA hình ảnh trên danh mục NPI như thế nào?
Gửi ảnh dạng base64 tới in.php, sau đó polling res.php để lấy kết quả — luồng xử lý giống hệt reCAPTCHA v2, chỉ khác tham số method.
Có cần khớp proxy theo từng bang khi tra cứu giá thuốc không?
Nên có, nếu cổng trả giá theo vùng địa lý — nhiều cổng giá thuốc Mỹ định giá theo IP hoặc mã zip.
Thu thập dữ liệu y tế công khai có vi phạm HIPAA hay Nghị định 13/2023/NĐ-CP không?
Không, nếu dữ liệu công khai và không gắn bệnh nhân cụ thể. HIPAA chỉ điều chỉnh PHI; đội tại Việt Nam vẫn nên ghi log nguồn dữ liệu theo tinh thần Nghị định 13/2023/NĐ-CP.
Hướng dẫn liên quan
- Tự động hóa cổng thông tin chính phủ Mỹ
- Thu thập dữ liệu nghiên cứu học thuật
- Đa dạng nguồn proxy khi thu thập dữ liệu quy mô lớn
Bắt đầu ngay
Đừng để CAPTCHA làm chậm pipeline dữ liệu y tế của bạn — lấy API key CaptchaAI và chạy thử script trong bài ngay hôm nay.