العودة إلى المدونة

بروكسي لجمع البيانات الطبية: كيفية جمع المعلومات بدون حظر

اكتشف كيفية استخراج البيانات الطبية من الدراسات السريرية وقواعد بيانات الأدوية والمجلات الطبية بأمان دون التعرض للحظر.

📅٢٠ رمضان ١٤٤٧ هـ
```html

جمع البيانات الطبية هو مهمة تتطلب نهجًا خاصًا في اختيار البروكسي. تستخدم البوابات الطبية، وقواعد بيانات التجارب السريرية والموارد الصيدلانية أنظمة حماية متقدمة ضد جمع البيانات الآلي. في هذه المقالة، سنستعرض كيفية إعداد البروكسي بشكل صحيح لجمع المعلومات الطبية بشكل آمن، وتجنب الحظر، وجمع البيانات المطلوبة بكفاءة.

لماذا تقوم المواقع الطبية بحظر جمع البيانات

تعتبر البوابات الطبية وقواعد البيانات حساسة بشكل خاص لجمع المعلومات الآلي لعدة أسباب. أولاً، يعمل العديد منها على أساس تجاري ويبيع الوصول إلى البيانات من خلال اشتراكات مدفوعة. يمكن أن ينتهك جمع البيانات الآلي شروط الاستخدام والاتفاقيات الترخيصية.

ثانياً، تحتوي البيانات الطبية غالبًا على معلومات حساسة محمية بموجب القانون (مثل HIPAA في الولايات المتحدة، وGDPR في أوروبا). يتعين على مالكي الموارد مراقبة الوصول إلى هذه البيانات ومنع انتشارها غير المصرح به. لذلك، يستخدمون أنظمة حماية متقدمة:

  • تحديد معدل الطلبات — تحديد عدد الطلبات من عنوان IP واحد في وحدة زمنية (عادة 10-50 طلبًا في الدقيقة)
  • تحليل بصمة المتصفح — تحليل خصائص المتصفح، وعناوين HTTP، وترتيب تحميل الموارد
  • CAPTCHA — أنظمة مثل reCAPTCHA v3 التي تعمل عند النشاط المشبوه
  • حظر IP — حظر مؤقت أو دائم لعناوين IP لمراكز البيانات
  • Cloudflare وما شابه — حماية ضد الروبوتات على مستوى CDN

السبب الثالث هو الحمل على الخوادم. تحتوي قواعد البيانات الطبية غالبًا على ملايين السجلات، ويمكن أن يؤدي جمع البيانات بشكل جماعي إلى خلق حمل كبير على البنية التحتية. لذلك، يكافح المسؤولون بنشاط ضد جمع البيانات الآلي، من خلال تتبع أنماط السلوك التي تميز الروبوتات: فترات زمنية متساوية بين الطلبات، وتصفح خطي للصفحات، وعدم وجود JavaScript وملفات تعريف الارتباط.

مهم: قبل البدء في جمع البيانات الطبية، تأكد من دراسة شروط استخدام الموقع والقوانين المعمول بها. قد تكون بعض البيانات محمية بموجب حقوق الطبع والنشر أو تحتوي على معلومات شخصية للمرضى. تأكد من أن نشاطك قانوني ولا ينتهك حقوق الأطراف الثالثة.

أي نوع من البروكسي يجب اختياره للبيانات الطبية

يعد اختيار نوع البروكسي أمرًا حيويًا لجمع البيانات الطبية بنجاح. تتطلب المصادر المختلفة نهجًا مختلفًا. دعونا نستعرض الأنواع الرئيسية للبروكسي ومدى ملاءمتها:

نوع البروكسي المزايا العيوب متى تستخدمه
بروكسي مراكز البيانات سرعة عالية (100+ ميغابت/ث)، تكلفة منخفضة، اتصال مستقر تكتشف بسهولة، وغالبًا ما يتم حظرها على المواقع المحمية قواعد بيانات مفتوحة بدون حماية صارمة (PubMed، WHO)
بروكسي سكنية عناوين IP حقيقية لمستخدمين منزليين، خطر منخفض للحظر، تتجاوز Cloudflare تكلفة أعلى، سرعة متغيرة، قد تكون غير مستقرة قواعد بيانات تجارية محمية (Elsevier، Springer)، مواقع مع Cloudflare
بروكسي موبايل أقصى ثقة (IP لمشغلي الهواتف المحمولة)، نادرًا ما يتم حظرها الأغلى، جغرافيا محدودة، قد تكون أبطأ موارد محمية بشكل خاص، عندما لا تساعد البروكسي السكنية
بروكسي ISP سرعة مراكز البيانات + ثقة السكنية، IP ثابتة تكلفة متوسطة، توفر محدود جمع البيانات على المدى الطويل من IP واحد، عندما تكون الاستقرار مطلوبة

بالنسبة لمعظم مهام جمع البيانات الطبية، يُوصى باستخدام البروكسي السكنية. إنها توفر توازنًا مثاليًا بين التكلفة والفعالية. بروكسي مراكز البيانات مناسبة فقط للمصادر المفتوحة بدون حماية. يجب استخدام بروكسي الموبايل في الحالات القصوى عندما لا تعمل الأنواع الأخرى.

توصيات للاختيار لمصادر معينة

  • PubMed، PubMed Central — بروكسي مراكز البيانات كافية، ولكن مع تحديد السرعة إلى 3 طلبات في الثانية
  • ClinicalTrials.gov — بروكسي مراكز البيانات، هناك API رسمي
  • Elsevier، Springer، Wiley — البروكسي السكنية إلزامية، تستخدم تحليل بصمة متقدمة
  • DrugBank، RxList — بروكسي سكنية، حماية نشطة ضد جمع البيانات
  • FDA، EMA قواعد البيانات — بروكسي مراكز البيانات مناسبة، ولكن مع سرعة جمع بيانات بطيئة

المصادر الرئيسية للبيانات الطبية وحمايتها

تتوزع البيانات الطبية عبر العديد من المصادر، ولكل منها خصائصها ومستوى حمايتها. سيساعد فهم هذه الخصائص على إعداد استراتيجية جمع البيانات بشكل صحيح.

قواعد البيانات الحكومية المفتوحة

PubMed/PubMed Central — أكبر قاعدة بيانات للنشر الطبي، تحتوي على أكثر من 35 مليون سجل. توفر المكتبة الطبية الوطنية الأمريكية (NLM) API رسمي E-utilities، وهو الطريقة المفضلة للوصول إلى البيانات. يمكن جمع البيانات من واجهة الويب، ولكنها محدودة بـ 3 طلبات في الثانية من IP واحد. يؤدي تجاوز الحد إلى حظر مؤقت لمدة 24 ساعة.

ClinicalTrials.gov — قاعدة بيانات التجارب السريرية، تحتوي على معلومات عن أكثر من 400,000 تجربة في 220 دولة. كما توفر API للوصول البرمجي. واجهة الويب محمية بتحديد معدل الطلبات — الحد الأقصى 100 طلب في 5 دقائق من IP واحد. تستخدم حماية أساسية ضد الروبوتات، ولكن بدون Cloudflare.

FDA Drugs Database — قاعدة بيانات الأدوية المعتمدة من FDA. الوصول المفتوح من خلال واجهة الويب وAPI openFDA. القيود: 240 طلبًا في الدقيقة للمستخدمين المجهولين، 1000 طلب في الدقيقة مع مفتاح API. الحظر نادر، ولكن قد يحدث مع جمع البيانات العدواني.

دور النشر العلمية التجارية

Elsevier (ScienceDirect) — واحدة من أكبر دور النشر للأدبيات العلمية. تستخدم حماية متعددة المستويات: Cloudflare، وتحليل بصمة المتصفح، وتحليل سلوك المستخدم. تكتشف أنماط التحميل الآلي: الوصول المتسلسل إلى المقالات، عدم وجود JavaScript، User-Agent غير المعتاد. عند اكتشاف جمع البيانات، تحظر IP على مستوى الحساب وقد تحظر المؤسسة بأكملها. من الضروري استخدام البروكسي السكنية مع تدوير كامل ومحاكاة المتصفح.

Springer Nature — حماية مماثلة، تراقب أيضًا سرعة تمرير الصفحات وحركة الماوس. تستخدم التعلم الآلي لاكتشاف الروبوتات. يُوصى بجمع بيانات لا تزيد عن 10-15 مقالة في الساعة من IP واحد، مع تأخيرات عشوائية بين الطلبات.

Wiley Online Library — حماية أقل عدوانية، ولكنها لا تزال تتطلب استخدام البروكسي. تسمح بحوالي 50 طلبًا في الساعة من IP واحد دون حظر. تستخدم ملفات تعريف الارتباط للجلسة لتتبع النشاط.

قواعد بيانات الأدوية

DrugBank — قاعدة بيانات شاملة للأدوية. النسخة المجانية محدودة بواجهة الويب، بينما توفر النسخة التجارية API وتصدير البيانات. النسخة الويب محمية بـ Cloudflare وتحديد معدل الطلبات — الحد الأقصى 20 طلبًا في الدقيقة. تكتشف الأتمتة من خلال عدم وجود ملفات تعريف الارتباط وJavaScript.

RxList، Drugs.com — أدلة شائعة للأدوية للمستهلكين. تستخدم Cloudflare وتكافح بنشاط ضد جمع البيانات. تحظر IP مراكز البيانات تقريبًا على الفور. تتطلب بروكسي سكنية وسرعة جمع بيانات بطيئة (5-10 صفحات في الدقيقة).

إعداد تدوير IP لجمع البيانات على المدى الطويل

يعد تدوير عناوين IP بشكل صحيح عاملًا رئيسيًا في نجاح جمع البيانات الطبية. هناك نهجان رئيسيان: التدوير على مستوى الطلبات والتدوير حسب الوقت.

التدوير على مستوى الطلبات

في هذا النهج، يتم إرسال كل طلب من خلال عنوان IP جديد. يقلل هذا من خطر الحظر، ولكنه قد يسبب مشاكل مع المواقع التي تتتبع الجلسات من خلال ملفات تعريف الارتباط. مناسب لجمع قوائم وفهارس حيث لا يتطلب الحفاظ على حالة الجلسة.

تقدم معظم مزودي البروكسي السكنية تدويرًا تلقائيًا من خلال نقطة نهاية خاصة. على سبيل المثال، عند استخدام نقطة نهاية البروكسي الدوارة، يحصل كل اتصال TCP جديد على IP جديد. يعمل هذا تلقائيًا مع مكتبات مثل requests في بايثون، حيث يتم إنشاء اتصال جديد لكل طلب بشكل افتراضي.

التدوير حسب الوقت (الجلسات الثابتة)

تسمح الجلسات الثابتة باستخدام عنوان IP واحد لفترة زمنية محددة (عادةً 5-30 دقيقة)، بعد ذلك يحدث تغيير تلقائي. هذا مفيد للمواقع التي تتطلب تسجيل الدخول أو تتتبع حالة الجلسة من خلال ملفات تعريف الارتباط. يمكنك جمع عدة صفحات من IP واحد، مما يحاكي سلوك المستخدم الحقيقي، ثم يتغير IP تلقائيًا.

بالنسبة للمواقع الطبية، يُوصى باستخدام الجلسات الثابتة لمدة 10-15 دقيقة. خلال هذا الوقت، يمكنك جمع 10-20 صفحة (اعتمادًا على التأخيرات)، بعد ذلك يتغير IP وتبدأ "جلسة جديدة". يبدو هذا طبيعيًا ويقلل من خطر الاكتشاف.

حجم مجموعة عناوين IP

لحفظ البيانات على المدى الطويل، يعد حجم مجموعة عناوين IP المتاحة أمرًا مهمًا. إذا كنت تستخدم نفس المجموعة من 100 IP على مدار أسبوع، قد تلاحظ الموقع نمطًا وتحظر جميع هذه العناوين. عادةً ما توفر البروكسي السكنية الوصول إلى ملايين IP، مما يقلل من احتمال إعادة استخدام نفس العنوان.

عند استخدام بروكسي مراكز البيانات، يُوصى بوجود مجموعة لا تقل عن 500-1000 IP لجمع بيانات متوسطة (10,000-50,000 صفحة في الشهر). لجمع بيانات على نطاق واسع (مئات الآلاف من الصفحات)، من الأفضل استخدام بروكسي سكنية مع مجموعاتها الضخمة من IP.

نصيحة حول التدوير لمصادر مختلفة:

  • PubMed — التدوير غير ضروري، يكفي 1 IP مع مراعاة تحديد معدل الطلبات
  • دور النشر التجارية — جلسات ثابتة لمدة 10-15 دقيقة، IP جديد كل 15-20 صفحة
  • قواعد بيانات الأدوية — تدوير لكل طلب أو جلسات ثابتة لمدة 5 دقائق
  • المواقع مع Cloudflare — الجلسات الثابتة إلزامية، التدوير على مستوى الطلبات لا يعمل

أمثلة على الكود بلغة بايثون لجمع البيانات باستخدام البروكسي

دعونا نستعرض أمثلة عملية لإعداد البروكسي لجمع البيانات الطبية باستخدام مكتبات بايثون الشهيرة. سنبدأ بمثال أساسي ونتدرج في تعقيده.

الإعداد الأساسي باستخدام مكتبة requests

import requests
from time import sleep
import random

# إعداد البروكسي (استبدل ببياناتك)
PROXY_HOST = "proxy.example.com"
PROXY_PORT = "8080"
PROXY_USER = "username"
PROXY_PASS = "password"

proxies = {
    'http': f'http://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}',
    'https': f'http://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}'
}

# العناوين لمحاكاة متصفح حقيقي
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
    'Accept-Language': 'en-US,en;q=0.9',
    'Accept-Encoding': 'gzip, deflate, br',
    'DNT': '1',
    'Connection': 'keep-alive',
    'Upgrade-Insecure-Requests': '1'
}

# مثال على طلب إلى PubMed
url = "https://pubmed.ncbi.nlm.nih.gov/?term=diabetes"

try:
    response = requests.get(url, proxies=proxies, headers=headers, timeout=30)
    print(f"رمز الحالة: {response.status_code}")
    print(f"طول المحتوى: {len(response.content)}")
    
    # إضافة تأخير بين الطلبات (ضروري لـ PubMed)
    sleep(random.uniform(1.0, 3.0))
    
except requests.exceptions.RequestException as e:
    print(f"خطأ: {e}")

إعداد متقدم مع تدوير ومنطق إعادة المحاولة

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
from time import sleep
import random

class ProxyRotator:
    def __init__(self, proxy_list):
        """
        proxy_list: قائمة من القواميس مع البروكسي
        [{'http': 'http://user:pass@host:port', 'https': '...'}, ...]
        """
        self.proxy_list = proxy_list
        self.current_index = 0
    
    def get_next_proxy(self):
        """الحصول على البروكسي التالي من القائمة"""
        proxy = self.proxy_list[self.current_index]
        self.current_index = (self.current_index + 1) % len(self.proxy_list)
        return proxy

def create_session_with_retries():
    """إنشاء جلسة مع إعادة المحاولات تلقائيًا عند الأخطاء"""
    session = requests.Session()
    
    # إعداد إعادة المحاولات التلقائية
    retry_strategy = Retry(
        total=3,  # الحد الأقصى 3 محاولات
        backoff_factor=1,  # التأخير بين المحاولات: 1، 2، 4 ثوانٍ
        status_forcelist=[429, 500, 502, 503, 504],  # الرموز لإعادة المحاولة
        allowed_methods=["GET", "POST"]
    )
    
    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("http://", adapter)
    session.mount("https://", adapter)
    
    return session

def scrape_with_rotation(urls, proxy_rotator):
    """جمع قائمة من URL مع تدوير البروكسي"""
    session = create_session_with_retries()
    results = []
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language': 'en-US,en;q=0.9',
    }
    
    for url in urls:
        # نحصل على بروكسي جديد لكل طلب
        proxy = proxy_rotator.get_next_proxy()
        
        try:
            response = session.get(
                url, 
                proxies=proxy, 
                headers=headers, 
                timeout=30
            )
            
            if response.status_code == 200:
                results.append({
                    'url': url,
                    'status': 'success',
                    'content_length': len(response.content)
                })
                print(f"✓ نجاح: {url}")
            else:
                results.append({
                    'url': url,
                    'status': 'failed',
                    'error': f"رمز الحالة: {response.status_code}"
                })
                print(f"✗ فشل: {url} (الحالة: {response.status_code})")
        
        except requests.exceptions.RequestException as e:
            results.append({
                'url': url,
                'status': 'error',
                'error': str(e)
            })
            print(f"✗ خطأ: {url} ({e})")
        
        # تأخير عشوائي بين الطلبات (مهم!)
        sleep(random.uniform(2.0, 5.0))
    
    return results

# مثال على الاستخدام
proxy_list = [
    {
        'http': 'http://user1:pass1@proxy1.example.com:8080',
        'https': 'http://user1:pass1@proxy1.example.com:8080'
    },
    {
        'http': 'http://user2:pass2@proxy2.example.com:8080',
        'https': 'http://user2:pass2@proxy2.example.com:8080'
    }
]

rotator = ProxyRotator(proxy_list)

urls_to_scrape = [
    "https://pubmed.ncbi.nlm.nih.gov/?term=diabetes",
    "https://pubmed.ncbi.nlm.nih.gov/?term=cancer",
    "https://pubmed.ncbi.nlm.nih.gov/?term=covid"
]

results = scrape_with_rotation(urls_to_scrape, rotator)

استخدام Selenium للمواقع التي تستخدم JavaScript

تستخدم العديد من المواقع الطبية الحديثة JavaScript لتحميل المحتوى. في هذه الحالات، يكون من الضروري استخدام متصفح بدون واجهة مستخدم:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

def create_proxy_driver(proxy_host, proxy_port, proxy_user, proxy_pass):
    """إنشاء Chrome WebDriver مع البروكسي"""
    
    chrome_options = Options()
    
    # وضع بدون واجهة مستخدم (headless)
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--disable-dev-shm-usage')
    
    # إعداد البروكسي
    chrome_options.add_argument(f'--proxy-server=http://{proxy_host}:{proxy_port}')
    
    # تعطيل الأتمتة (مهم لتجاوز الاكتشاف)
    chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
    chrome_options.add_experimental_option('useAutomationExtension', False)
    
    # User-Agent
    chrome_options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36')
    
    driver = webdriver.Chrome(options=chrome_options)
    
    # للبروكسي مع المصادقة، يجب استخدام ملحق
    # أو إعداد عبر capabilities (خيار أكثر تعقيدًا)
    
    return driver

def scrape_with_selenium(url, driver):
    """جمع صفحة مع انتظار تحميل JavaScript"""
    
    driver.get(url)
    
    # انتظار تحميل العنصر (مثل نتائج البحث)
    try:
        wait = WebDriverWait(driver, 10)
        results = wait.until(
            EC.presence_of_element_located((By.CLASS_NAME, "results-article"))
        )
        
        # استخراج البيانات
        articles = driver.find_elements(By.CLASS_NAME, "results-article")
        
        data = []
        for article in articles:
            try:
                title = article.find_element(By.CLASS_NAME, "docsum-title").text
                authors = article.find_element(By.CLASS_NAME, "docsum-authors").text
                
                data.append({
                    'title': title,
                    'authors': authors
                })
            except:
                continue
        
        return data
        
    except Exception as e:
        print(f"خطأ في انتظار العناصر: {e}")
        return []

# مثال على الاستخدام
proxy_host = "proxy.example.com"
proxy_port = "8080"
proxy_user = "username"
proxy_pass = "password"

driver = create_proxy_driver(proxy_host, proxy_port, proxy_user, proxy_pass)

try:
    url = "https://pubmed.ncbi.nlm.nih.gov/?term=diabetes"
    results = scrape_with_selenium(url, driver)
    
    for result in results:
        print(f"العنوان: {result['title']}")
        print(f"المؤلفون: {result['authors']}\n")
        
finally:
    driver.quit()

التحكم في سرعة الطلبات وتجاوز قيود السرعة

تحديد معدل الطلبات هو أحد الحمايات الأساسية للمواقع الطبية ضد جمع البيانات. إن إعداد سرعة الطلبات بشكل صحيح أمر بالغ الأهمية لجمع البيانات على المدى الطويل دون حظر.

تحديد السرعة الآمنة

الخطوة الأولى هي تحديد الحدود الخاصة بالموقع المحدد. يمكن القيام بذلك تجريبيًا، من خلال زيادة سرعة الطلبات تدريجيًا حتى ظهور أخطاء 429 (طلبات كثيرة جدًا) أو الحظر. بالنسبة لمعظم المواقع الطبية، القيم الآمنة هي:

  • PubMed — الحد الأقصى 3 طلبات في الثانية (توصية رسمية)
  • ClinicalTrials.gov — 20 طلبًا في الدقيقة آمن، حتى 100 في 5 دقائق مقبول
  • دور النشر التجارية — 10-15 طلبًا في الساعة من IP واحد
  • قواعد بيانات الأدوية — 5-10 طلبات في الدقيقة

تنفيذ محدد السرعة في بايثون

import time
from collections import deque

class RateLimiter:
    def __init__(self, max_calls, period):
        """
        max_calls: الحد الأقصى لعدد المكالمات
        period: فترة زمنية بالثواني
        على سبيل المثال: RateLimiter(3, 1) = 3 طلبات في الثانية
        """
        self.max_calls = max_calls
        self.period = period
        self.calls = deque()
    
    def __call__(self, func):
        """ديكور لتحديد سرعة استدعاء الدالة"""
        def wrapper(*args, **kwargs):
            now = time.time()
            
            # إزالة المكالمات القديمة التي تتجاوز الفترة
            while self.calls and self.calls[0] < now - self.period:
                self.calls.popleft()
            
            # إذا وصلنا إلى الحد، ننتظر
            if len(self.calls) >= self.max_calls:
                sleep_time = self.period - (now - self.calls[0])
                if sleep_time > 0:
                    print(f"تم الوصول إلى حد السرعة، نائم لمدة {sleep_time:.2f}s")
                    time.sleep(sleep_time)
                    # تنظيف بعد الانتظار
                    self.calls.clear()
            
            # تسجيل وقت الاستدعاء
            self.calls.append(time.time())
            
            # تنفيذ الدالة
            return func(*args, **kwargs)
        
        return wrapper

# مثال على الاستخدام
@RateLimiter(max_calls=3, period=1)  # 3 طلبات في الثانية
def fetch_pubmed_page(url):
    response = requests.get(url, headers=headers, proxies=proxies)
    return response

# الآن الدالة تلتزم تلقائيًا بحد السرعة
for i in range(10):
    result = fetch_pubmed_page(f"https://pubmed.ncbi.nlm.nih.gov/?term=test&page={i}")
    print(f"تم جلب الصفحة {i}")

تحديد السرعة التكيفية

نهج أكثر تقدمًا هو تغيير السرعة بشكل تكيفي اعتمادًا على استجابات الخادم. إذا تلقينا أخطاء 429 أو 503، نقوم تلقائيًا بتقليل السرعة:

import time
import random

class AdaptiveRateLimiter:
    def __init__(self, initial_delay=1.0, max_delay=60.0):
        self.current_delay = initial_delay
        self.initial_delay = initial_delay
        self.max_delay = max_delay
        self.success_count = 0
    
    def wait(self):
        """انتظار قبل الطلب التالي"""
        # إضافة العشوائية للطبيعية
        actual_delay = self.current_delay * random.uniform(0.8, 1.2)
        time.sleep(actual_delay)
    
    def on_success(self):
        """يتم استدعاؤه عند نجاح الطلب"""
        self.success_count += 1
        
        # بعد 10 طلبات ناجحة، نسرع قليلاً
        if self.success_count >= 10:
            self.current_delay = max(
                self.initial_delay,
                self.current_delay * 0.9
            )
            self.success_count = 0
    
    def on_rate_limit(self):
        """يتم استدعاؤه عند تلقي 429 أو أخطاء مشابهة"""
        # نضاعف التأخير، ولكن لا يزيد عن الحد الأقصى
        self.current_delay = min(
            self.current_delay * 2,
            self.max_delay
        )
        self.success_count = 0
        print(f"تم الوصول إلى حد السرعة! زيادة التأخير إلى {self.current_delay:.2f}s")
    
    def on_error(self):
        """يتم استدعاؤه عند حدوث أخطاء أخرى"""
        # نزيد التأخير قليلاً
        self.current_delay = min(
            self.current_delay * 1.5,
            self.max_delay
        )
        self.success_count = 0

# مثال على الاستخدام
limiter = AdaptiveRateLimiter(initial_delay=2.0, max_delay=30.0)

for url in urls_to_scrape:
    limiter.wait()
    
    try:
        response = requests.get(url, proxies=proxies, headers=headers)
        
        if response.status_code == 200:
            limiter.on_success()
            # معالجة البيانات
            
        elif response.status_code == 429:
            limiter.on_rate_limit()
            # إعادة المحاولة لاحقًا
            
        else:
            limiter.on_error()
            
    except requests.exceptions.RequestException:
        limiter.on_error()

العناوين الصحيحة وUser-Agent للمواقع الطبية

تقوم المواقع الطبية بتحليل عناوين HTTP لاكتشاف الروبوتات. العناوين غير الصحيحة أو المفقودة هي سبب شائع للحظر حتى عند استخدام بروكسي عالية الجودة.

العناوين الإلزامية

الحد الأدنى من مجموعة العناوين التي يجب أن تكون موجودة في كل طلب:

headers = {
    # User-Agent — يجب أن يكون متصفحًا حديثًا
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    
    # Accept — أنواع المحتوى التي يقبلها المتصفح
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
    
    # Accept-Language — لغة المستخدم
    'Accept-Language': 'en-US,en;q=0.9',
    
    # Accept-Encoding — دعم الضغط
    'Accept-Encoding': 'gzip, deflate, br',
    
    # Connection — الحفاظ على الاتصال
    'Connection': 'keep-alive',
    
    # Upgrade-Insecure-Requests — الانتقال التلقائي إلى HTTPS
    'Upgrade-Insecure-Requests': '1',
    
    # DNT — Do Not Track (اختياري، ولكن يضيف واقعية)
    'DNT': '1',
    
    # عناوين Sec-Fetch-* (مهمة للمتصفحات الحديثة)
    'Sec-Fetch-Dest': 'document',
    'Sec-Fetch-Mode': 'navigate',
    'Sec-Fetch-Site': 'none',
    'Sec-Fetch-User': '?1',
    
    # Cache-Control
    'Cache-Control': 'max-age=0'
}

تدوير User-Agent

يمكن أن يكون استخدام نفس User-Agent مريبًا. يُوصى بالتدوير بين عدة متصفحات حديثة:

import random

USER_AGENTS = [
    # Chrome على Windows
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    
    # Chrome على Mac
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    
    # Firefox على Windows
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
    
    # Firefox على Mac
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0',
    
    # Safari على Mac
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15',
    
    # Edge على Windows
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0'
]

def get_random_headers():
    """الحصول على عناوين مع User-Agent عشوائي"""
    return {
        'User-Agent': random.choice(USER_AGENTS),
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Language': 'en-US,en;q=0.9',
        'Accept-Encoding': 'gzip, deflate, br',
        'Connection': 'keep-alive',
        'Upgrade-Insecure-Requests': '1',
        'DNT': '1'
    }

# الاستخدام
for url in urls:
    headers = get_random_headers()
    response = requests.get(url, headers=headers, proxies=proxies)

Referer وOrigin للنماذج

عند العمل مع نماذج البحث أو إرسال طلبات POST، تأكد من إضافة عناوين Referer وOrigin:

# لطلبات POST إلى نموذج البحث
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en-US,en;q=0.9',
    'Accept-Encoding': 'gzip, deflate, br',
    'Content-Type': 'application/x-www-form-urlencoded',
    'Origin': 'https://example.com',
    'Referer': 'https://example.com/search',
    'Connection': 'keep-alive'
}

# طلب POST مع بيانات النموذج
data = {
    'query': 'diabetes',
    'page': '1'
}

response = requests.post(
    'https://example.com/search',
    headers=headers,
    data=data,
    proxies=proxies
)

المشاكل الشائعة وحلولها

عند جمع البيانات الطبية، تظهر مشاكل محددة. دعونا نستعرض أكثرها شيوعًا وطرق حلها.

المشكلة: Cloudflare يحظر جميع الطلبات

الأعراض: تحصل على صفحة تحتوي على نص "Checking your browser" أو خطأ 403 Forbidden مع ذكر Cloudflare.

الحل:

  • استخدم البروكسي السكنية بدلاً من مراكز البيانات — يحظر Cloudflare IP مراكز البيانات افتراضيًا
  • انتقل إلى Selenium أو Puppeteer — المتصفحات بدون واجهة مستخدم تتجاوز اختبارات Cloudflare بشكل أفضل
  • استخدم مكتبة cloudscraper لبايثون — تتجاوز تلقائيًا الحماية الأساسية لـ Cloudflare
  • قم بتمكين ملفات تعريف الارتباط وJavaScript — يتحقق Cloudflare من وجودها
  • أضف تحليل بصمة TLS — استخدم curl_cffi لمحاكاة متصفح حقيقي على مستوى TLS

المشكلة: أتلقى خطأ 429 Too Many Requests

الأعراض: بعد عدة طلبات ناجحة، يبدأ الخادم في إرجاع 429.

الحل:

  • زد التأخير بين الطلبات — حاول البدء من 3-5 ثوانٍ
  • قم بتمكين تدوير IP — كل طلب عبر IP جديد يزيل قيود السرعة
  • تحقق من عنوان Retry-After في استجابة 429 — يشير إلى عدد الثواني التي يجب الانتظار
  • استخدم تأخيرًا أسيًا عند إعادة المحاولات — 1s، 2s، 4s، 8s، إلخ.

المشكلة: البروكسي تعمل ببطء أو تتعطل كثيرًا

الأعراض: أخطاء المهلة، تحميل الصفحات ببطء شديد، انقطاع الاتصال.

الحل:

  • زد المهلة في الطلبات إلى 30-60 ثانية — قد تكون البروكسي السكنية أبطأ
  • استخدم بروكسي قريبة جغرافيًا — إذا كنت تجمع بيانات من موقع أوروبي، استخدم IP أوروبي
  • تحقق من جودة مزود البروكسي — البروكسي الرخيصة غالبًا ما تكون غير مستقرة
  • أضف منطق إعادة المحاولة — أعد الطلب تلقائيًا عند حدوث خطأ في الاتصال
  • استخدم تجميع الاتصالات — أعد استخدام اتصالات TCP عبر requests.Session()

المشكلة: الموقع يتطلب تسجيل الدخول أو اشتراك

الأعراض: الوصول إلى النصوص الكاملة للمقالات محدود، يتطلب تسجيل الدخول.

الحل:

  • استخدم الوصول المؤسسي — العديد من الجامعات والمستشفيات لديها اشتراكات
  • تحقق من وجود نسخ مفتوحة الوصول — العديد من المقالات متاحة مجانًا عبر المستودعات
  • استخدم API بدلاً من الجمع — تقدم بعض دور النشر API للباحثين
  • اجمع فقط البيانات الوصفية (العناوين، المؤلفون، الملخصات) — عادة ما تكون متاحة مجانًا

المشكلة: محتوى JavaScript لا يتم تحميله

الأعراض: لا توجد بيانات مطلوبة في HTML، فقط دوائر تحميل أو حاويات فارغة.

الحل:

  • انتقل إلى Selenium/Puppeteer — تقوم بتنفيذ JavaScript
  • ابحث عن نقطة نهاية API — افتح أدوات المطور في المتصفح، علامة التبويب الشبكة، وابحث عن طلبات XHR مع البيانات
  • استخدم requests-html — مكتبة مع...
```