بيانات الرعاية الصحية العامة متاحة للجميع نظريًا — أدلة الأطباء المرخّصين، وأسعار الأدوية المنشورة، وسجلات التجارب السريرية — لكن البوابات التي تنشرها تضع اختبار CAPTCHA أمام كل عملية بحث لتعطيل الجمع الآلي. الحل عمليًا مباشر: يرسل سكربت الأتمتة اختبار CAPTCHA إلى CaptchaAI، ويستقبل الرمز المحلول، ثم يكمل البحث كأنه مستخدم عادي. تشرح هذه المقالة كيف تبني هذا المسار في Python لأدلة مقدمي الخدمة وأسعار الأدوية وبيانات التجارب السريرية، مع الحفاظ على حدود واضحة لا تتعدى البيانات المتاحة للعامة.
أنواع الاختبارات التي تستخدمها بوابات الصحة
تعتمد بوابات الرعاية الصحية على نوعين شائعين من التحقق: reCAPTCHA v2 على صفحات التسعير وسجلات التجارب، وصور CAPTCHA من نوع Image/OCR على أدلة الموفرين ومجالس الترخيص، مع ظهور Cloudflare Turnstile أحيانًا على صفحات تقييم جودة المستشفيات. يتعامل CaptchaAI مع هذه الأنواع الثلاثة عبر نمط موحّد:
- الإرسال — أرسل تفاصيل الاختبار إلى نقطة النهاية
in.phpواحصل على معرّف المهمة. - الاستطلاع الدوري — استفسر عن النتيجة من
res.phpحتى يجهز الرمز. - الاستخدام — مرّر الرمز في حقل الإرسال:
g-recaptcha-responseلاختبارات reCAPTCHA، أو حقل الصورة لاختبارات Image/OCR.
ولأن فوترة CaptchaAI قائمة على عدد الـ Thread المتزامنة لا على كل عملية حل، يمكنك تشغيل عشرات عمليات البحث بالتوازي دون رسوم إضافية لكل اختبار.
قبل أن تبدأ
تحتاج قبل تشغيل الأمثلة إلى العناصر التالية:
- مفتاح
YOUR_API_KEYمن لوحة تحكم CaptchaAI. - بيئة Python مع مكتبتَي
requestsوbeautifulsoup4. - خادم وسيط سكني عند التعامل مع بوابات تسعير حساسة للموقع الجغرافي.
أين يظهر اختبار CAPTCHA في مصادر الصحة
| المصدر | نوع التحقق | البيانات | حالة الاستخدام |
|---|---|---|---|
| أدلة الموفرين (NPI) | صورة CAPTCHA | بحث عن طبيب أو منشأة | كفاية الشبكة |
| بوابات تسعير الأدوية | reCAPTCHA v2 | أسعار الأدوية | شفافية الأسعار |
| سجلات التجارب السريرية | reCAPTCHA v2 | بيانات التجربة والنتائج | تحليل البحوث |
| صيغ التأمين | reCAPTCHA v2 | قوائم تغطية الأدوية | مقارنة الوصفات |
| مجالس الترخيص | صورة CAPTCHA | التحقق من الترخيص | فحص الاعتماد |
| تقييمات جودة المستشفيات | Cloudflare Turnstile | مقاييس الجودة | تحليل الأداء |
بناء أداة جمع أدلة الموفرين
الصنف التالي يجمع منطق الجلسة والوكيل ومعالجة الاختبار في مكان واحد، ويتعامل مع مسار reCAPTCHA (عبر sitekey) ومسار صورة CAPTCHA الاحتياطي في الدالة نفسها:
مكشطة دليل الموفر
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",
)
كيف يعمل الصنف عمليًا
يوفّر HealthcareDataCollector ثلاث وظائف مترابطة: search_providers تفتح صفحة الدليل وتحلّ الاختبار الظاهر — سواء كان reCAPTCHA v2 عبر sitekey أو صورة CAPTCHA يتم التقاطها من الصفحة — قبل إرسال معايير البحث؛ وlookup_drug_prices تحلّ reCAPTCHA على صفحة التسعير وتعيد قائمة الصيدليات والأسعار؛ وbatch_provider_lookup تدور على كل تخصص مقابل كل موقع وتصدّر النتائج إلى ملف CSV. لاحظ أن معالج الأخطاء يسجّل كل تركيبة تخصص/موقع فشلت ويتابع بقية الدفعة بدل التوقف الكامل.
اختيار عدد الـ Threads والخطة المناسبة
تخيّل فريق تقنية صحية في الرياض أو القاهرة يبني أداة لمقارنة أسعار الأدوية العامة عبر مئات الصيدليات وعشرات التخصصات. الدالة batch_provider_lookup تعني عشرات — وربما مئات — عمليات الحل المتزامنة، وهنا يظهر أثر نموذج الـ Thread المتزامن:
- BASIC ($15 شهريًا، 5 threads) — يكفي لاختبار أولي محدود أو دفعة صغيرة.
- ADVANCE ($90 شهريًا، 50 thread) — مناسب للدفعات المتوسطة عبر عدة تخصصات ومناطق.
- PREMIUM ($170 شهريًا، 100 thread) — يوفّر توازيًا أعلى للدفعات الكبيرة المتكررة.
كل الخطط تشمل عددًا غير محدود من عمليات الحل لكل thread خلال الشهر، فالتكلفة ثابتة ومتوقعة بصرف النظر عن حجم البحث — على عكس التسعير بالحل الواحد الذي يرتفع مع كل عملية.
مطابقة موقع الوكيل بالتسعير الجغرافي
أسعار الأدوية غالبًا محسوبة حسب الرمز البريدي، لذا يجب أن يطابق موقع الخادم الوسيط (الوكيل السكني) الرمز البريدي المطلوب في lookup_drug_prices، وإلا حصلت على أسعار منطقة مختلفة. مرّر بيانات الوكيل عند إنشاء الصنف كما في مثال الاستخدام أعلاه.
جمع بيانات التجارب السريرية
الدالة التالية تعيد استخدام الصنف نفسه لجمع البيانات الوصفية العامة للتجارب السريرية — العنوان والحالة والراعي والمرحلة وعدد المشاركين — بعد حل reCAPTCHA على صفحة البحث:
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
حدود الخصوصية: ما الذي يُجمع وما الذي يُترك
القاعدة الحاكمة بسيطة: اجمع البيانات المنشورة للعامة فقط، ولا تقترب من المعلومات الصحية الخاصة بالمريض. الجدول التالي يلخّص مستوى الحساسية والتوصية لكل نوع:
| نوع البيانات | الحساسية | التوصية |
|---|---|---|
| أدلة الموفرين | منخفضة (معلومات عامة) | آمنة للجمع عمومًا |
| أسعار الأدوية | منخفضة (تسعير منشور) | مسموحة لأغراض الشفافية |
| البيانات الوصفية للتجارب السريرية | منخفضة (سجلات عامة) | مناسبة للاستخدام البحثي |
| مراجعات المرضى | متوسطة | أخفِ الهوية قبل التحليل |
| تفاصيل خطط التأمين | منخفضة (معدلات منشورة) | مسموحة للمقارنة |
تنبيه مهم: لا تحاول مطلقًا جمع المعلومات الصحية المحمية (PHI) أو سجلات المرضى الفردية. اقتصر على البيانات المتاحة للعامة وغير المرتبطة بمريض بعينه.
معالجة المشكلات الشائعة
| المشكلة | السبب المحتمل | الإجراء |
|---|---|---|
| صورة CAPTCHA غير قابلة للقراءة | جودة صورة منخفضة | أعد المحاولة — يُنشأ رمز صورة جديد |
| بحث الموفرين يعود فارغًا | حُظر الإرسال قبل حل الاختبار | حلّ CAPTCHA قبل إرسال النموذج |
| اختلاف سعر الدواء حسب المنطقة | تسعير مبني على الموقع الجغرافي | طابق موقع الوكيل مع الرمز البريدي |
| انتهاء صلاحية الجلسة عبر عدة صفحات | مهلة انتهاء البوابة | أكمل عمليات البحث بسرعة ضمن الجلسة |
| تحديد معدل الطلبات في الدفعات | عدد طلبات كبير جدًا | أضف تأخيرًا 5–10 ثوانٍ بين الطلبات |
الأسئلة الشائعة
ما الخطة المناسبة لعمليات البحث الدفعية الكبيرة؟
يتحدّد ذلك بعدد عمليات الحل المتزامنة لا بإجماليها. للدفعات الصغيرة تكفي BASIC ($15 شهريًا، 5 threads)، وللدفعات المتوسطة عبر عدة تخصصات ومناطق تناسب ADVANCE ($90 شهريًا، 50 thread)، بينما توفّر PREMIUM ($170 شهريًا، 100 thread) توازيًا أعلى — وكل الخطط بعمليات حل غير محدودة لكل thread.
كيف أضمن دقة أسعار الأدوية المرتبطة بالموقع؟
لأن التسعير غالبًا جغرافي، اجعل موقع الخادم الوسيط مطابقًا للرمز البريدي الذي تبحث عنه. استخدام وكيل من منطقة مختلفة يعيد أسعارًا لا تخص السوق المستهدف.
هل يتعامل CaptchaAI مع صور CAPTCHA و reCAPTCHA v2 في المسار نفسه؟
نعم. الدالة search_providers تختار المسار تلقائيًا: إن توفّر sitekey تعالج reCAPTCHA v2، وإلا التقطت صورة CAPTCHA من الصفحة وأرسلتها بطريقة base64. كلا النوعين مدعوم بشكل كامل.
ماذا أفعل عند انتهاء صلاحية الجلسة أثناء البحث متعدد الصفحات؟
أكمل خطوات البحث المرتبطة داخل جلسة requests.Session واحدة وبسرعة معقولة قبل أن تنتهي مهلة البوابة، وأعد فتح الجلسة عند الحاجة بدل الاعتماد على جلسة قديمة.
أدلة ذات صلة
- أتمتة البوابات الحكومية وحل اختبارات CAPTCHA
- استخراج بيانات الأبحاث الأكاديمية من مواقع محمية بـ CAPTCHA
ابدأ بجمع بيانات الرعاية الصحية العامة بكفاءة — أنشئ مفتاح CaptchaAI وأتمتة عمليات البحث عن الأطباء والأسعار خلف اختبارات CAPTCHA.