Назад к блогу

Прокси для парсинга медицинских данных: как собирать информацию без блокировок

Узнайте, как безопасно парсить медицинские данные с клинических исследований, баз данных лекарств и медицинских журналов, не получая блокировки.

📅9 марта 2026 г.

Парсинг медицинских данных — задача, требующая особого подхода к выбору прокси. Медицинские порталы, базы данных клинических исследований и фармацевтические ресурсы используют продвинутые системы защиты от автоматизированного сбора данных. В этой статье разберём, как правильно настроить прокси для безопасного парсинга медицинской информации, избежать блокировок и собрать нужные данные эффективно.

Почему медицинские сайты блокируют парсинг

Медицинские порталы и базы данных особенно чувствительны к автоматизированному сбору информации по нескольким причинам. Во-первых, многие из них работают на коммерческой основе и продают доступ к данным через платные подписки. Автоматический парсинг может нарушать условия использования и лицензионные соглашения.

Во-вторых, медицинские данные часто содержат конфиденциальную информацию, защищённую законодательством (HIPAA в США, GDPR в Европе). Владельцы ресурсов обязаны контролировать доступ к таким данным и предотвращать их несанкционированное распространение. Поэтому они используют продвинутые системы защиты:

  • Rate limiting — ограничение количества запросов с одного IP-адреса в единицу времени (обычно 10-50 запросов в минуту)
  • Fingerprinting — анализ характеристик браузера, заголовков HTTP, порядка загрузки ресурсов
  • CAPTCHA — системы типа reCAPTCHA v3, которые срабатывают при подозрительной активности
  • IP-блокировки — временная или постоянная блокировка IP-адресов дата-центров
  • Cloudflare и аналоги — защита от ботов на уровне CDN

Третья причина — нагрузка на серверы. Медицинские базы данных часто содержат миллионы записей, и массовый парсинг может создать значительную нагрузку на инфраструктуру. Поэтому администраторы активно борются с автоматизированным сбором данных, отслеживая паттерны поведения, характерные для ботов: одинаковые интервалы между запросами, линейный обход страниц, отсутствие JavaScript и cookies.

Важно: Перед началом парсинга медицинских данных обязательно изучите условия использования сайта и применимое законодательство. Некоторые данные могут быть защищены авторским правом или содержать персональную информацию пациентов. Убедитесь, что ваша деятельность законна и не нарушает права третьих лиц.

Какой тип прокси выбрать для медицинских данных

Выбор типа прокси критически важен для успешного парсинга медицинских данных. Разные источники требуют разного подхода. Рассмотрим основные типы прокси и их применимость:

Тип прокси Преимущества Недостатки Когда использовать
Прокси дата-центров Высокая скорость (100+ Мбит/с), низкая стоимость, стабильное соединение Легко детектируются, часто заблокированы на защищённых сайтах Открытые базы данных без строгой защиты (PubMed, WHO)
Резидентные прокси Реальные IP домашних пользователей, низкий риск блокировки, проходят Cloudflare Выше стоимость, переменная скорость, могут быть нестабильны Защищённые коммерческие базы (Elsevier, Springer), сайты с Cloudflare
Мобильные прокси Максимальное доверие (IP мобильных операторов), практически не блокируются Самые дорогие, ограниченная география, могут быть медленнее Особо защищённые ресурсы, когда резидентные прокси не помогают
ISP прокси Скорость дата-центров + доверие резидентных, статичные IP Средняя стоимость, ограниченная доступность Долгосрочный парсинг с одного IP, когда нужна стабильность

Для большинства задач парсинга медицинских данных рекомендуется использовать резидентные прокси. Они обеспечивают оптимальный баланс между стоимостью и эффективностью. Прокси дата-центров подходят только для открытых источников без защиты. Мобильные прокси стоит использовать в крайних случаях, когда другие типы не работают.

Рекомендации по выбору для конкретных источников

  • PubMed, PubMed Central — прокси дата-центров достаточно, но с ограничением скорости до 3 запросов в секунду
  • ClinicalTrials.gov — прокси дата-центров, есть официальный API
  • Elsevier, Springer, Wiley — резидентные прокси обязательны, используют продвинутый fingerprinting
  • DrugBank, RxList — резидентные прокси, активная защита от парсинга
  • FDA, EMA базы данных — прокси дата-центров подходят, но с медленной скоростью парсинга

Основные источники медицинских данных и их защита

Медицинские данные распределены по множеству источников, каждый из которых имеет свою специфику и уровень защиты. Понимание этих особенностей поможет правильно настроить стратегию парсинга.

Открытые государственные базы данных

PubMed/PubMed Central — крупнейшая база медицинских публикаций, содержит более 35 миллионов записей. Национальная медицинская библиотека США (NLM) предоставляет официальный API E-utilities, который является предпочтительным способом доступа к данным. Прямой парсинг веб-интерфейса возможен, но ограничен 3 запросами в секунду с одного IP. Превышение лимита приводит к временной блокировке на 24 часа.

ClinicalTrials.gov — база данных клинических исследований, содержит информацию о более чем 400,000 исследованиях в 220 странах. Также предоставляет API для программного доступа. Веб-интерфейс защищён rate limiting — максимум 100 запросов в 5 минут с одного IP. Используется базовая защита от ботов, но без Cloudflare.

FDA Drugs Database — база данных одобренных лекарственных препаратов FDA. Открытый доступ через веб-интерфейс и API openFDA. Ограничения: 240 запросов в минуту для анонимных пользователей, 1000 запросов в минуту с API-ключом. Блокировки редки, но при агрессивном парсинге возможны.

Коммерческие научные издательства

Elsevier (ScienceDirect) — один из крупнейших издателей научной литературы. Использует многоуровневую защиту: Cloudflare, fingerprinting браузера, анализ поведения пользователя. Детектирует паттерны автоматического скачивания: последовательный доступ к статьям, отсутствие JavaScript, нетипичные User-Agent. При обнаружении парсинга блокирует IP на уровне аккаунта и может заблокировать весь институт. Обязательно использование резидентных прокси с ротацией и полной эмуляцией браузера.

Springer Nature — аналогичная защита, дополнительно отслеживает скорость скроллинга страниц и движения мыши. Использует machine learning для детекции ботов. Рекомендуется парсить не более 10-15 статей в час с одного IP, с рандомизированными задержками между запросами.

Wiley Online Library — менее агрессивная защита, но всё равно требует использования прокси. Допускает около 50 запросов в час с одного IP без блокировки. Использует session cookies для отслеживания активности.

Фармацевтические базы данных

DrugBank — комплексная база данных лекарственных препаратов. Бесплатная версия ограничена веб-интерфейсом, коммерческая предоставляет API и выгрузки данных. Веб-версия защищена Cloudflare и rate limiting — максимум 20 запросов в минуту. Детектирует автоматизацию по отсутствию cookies и JavaScript.

RxList, Drugs.com — популярные справочники лекарств для потребителей. Используют Cloudflare и активно борются с парсингом. Блокируют IP дата-центров практически моментально. Требуются резидентные прокси и медленная скорость парсинга (5-10 страниц в минуту).

Настройка ротации IP для долгосрочного парсинга

Правильная ротация IP-адресов — ключевой фактор успешного парсинга медицинских данных. Существует два основных подхода: ротация на уровне запросов и ротация по времени.

Ротация на уровне запросов

При этом подходе каждый запрос отправляется через новый IP-адрес. Это максимально снижает риск блокировки, но может вызвать проблемы с сайтами, которые отслеживают сессии через cookies. Подходит для парсинга списков и каталогов, где не требуется поддержание состояния сессии.

Большинство провайдеров резидентных прокси предоставляют автоматическую ротацию через специальный endpoint. Например, при использовании rotating proxy endpoint каждый новый TCP-коннект получает новый IP. Это работает автоматически с библиотеками типа requests в Python, так как по умолчанию создаётся новое соединение для каждого запроса.

Ротация по времени (sticky sessions)

Sticky sessions позволяют использовать один IP-адрес в течение определённого времени (обычно 5-30 минут), после чего происходит автоматическая смена. Это полезно для сайтов, требующих авторизации или отслеживающих состояние сессии через cookies. Вы можете парсить несколько страниц с одного IP, имитируя поведение реального пользователя, а затем IP автоматически меняется.

Для медицинских сайтов рекомендуется использовать sticky sessions длительностью 10-15 минут. За это время можно спарсить 10-20 страниц (в зависимости от задержек), после чего IP меняется, и вы начинаете "новую сессию". Это выглядит естественно и снижает риск детекции.

Размер пула IP-адресов

Для долгосрочного парсинга важен размер пула доступных IP-адресов. Если вы используете один и тот же набор из 100 IP в течение недели, сайт может заметить паттерн и заблокировать все эти адреса. Резидентные прокси обычно предоставляют доступ к миллионам IP, что практически исключает повторное использование одного и того же адреса.

При использовании прокси дата-центров рекомендуется иметь пул минимум из 500-1000 IP для парсинга среднего объёма (10,000-50,000 страниц в месяц). Для крупномасштабного парсинга (сотни тысяч страниц) лучше использовать резидентные прокси с их огромными пулами IP.

Совет по ротации для разных источников:

  • PubMed — ротация не обязательна, достаточно 1 IP с соблюдением rate limit
  • Коммерческие издательства — sticky sessions 10-15 минут, новый IP каждые 15-20 страниц
  • Фармацевтические базы — ротация на каждый запрос или sticky sessions 5 минут
  • Сайты с Cloudflare — sticky sessions обязательны, ротация на уровне запросов не работает

Примеры кода на Python для парсинга с прокси

Рассмотрим практические примеры настройки прокси для парсинга медицинских данных с использованием популярных Python-библиотек. Начнём с базового примера и постепенно усложним его.

Базовая настройка с библиотекой 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"Status code: {response.status_code}")
    print(f"Content length: {len(response.content)}")
    
    # Добавляем задержку между запросами (обязательно для PubMed)
    sleep(random.uniform(1.0, 3.0))
    
except requests.exceptions.RequestException as e:
    print(f"Error: {e}")

Продвинутая настройка с ротацией и retry logic

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"✓ Success: {url}")
            else:
                results.append({
                    'url': url,
                    'status': 'failed',
                    'error': f"Status code: {response.status_code}"
                })
                print(f"✗ Failed: {url} (Status: {response.status_code})")
        
        except requests.exceptions.RequestException as e:
            results.append({
                'url': url,
                'status': 'error',
                'error': str(e)
            })
            print(f"✗ Error: {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 для загрузки контента. В таких случаях необходим headless-браузер:

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 режим (без GUI)
    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"Error waiting for elements: {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"Title: {result['title']}")
        print(f"Authors: {result['authors']}\n")
        
finally:
    driver.quit()

Контроль скорости запросов и обход rate limiting

Rate limiting — одна из основных защит медицинских сайтов от парсинга. Правильная настройка скорости запросов критически важна для долгосрочного парсинга без блокировок.

Определение безопасной скорости

Первый шаг — определить лимиты конкретного сайта. Это можно сделать экспериментально, постепенно увеличивая скорость запросов до появления ошибок 429 (Too Many Requests) или блокировки. Для большинства медицинских сайтов безопасные значения:

  • PubMed — максимум 3 запроса в секунду (официальная рекомендация)
  • ClinicalTrials.gov — 20 запросов в минуту безопасно, до 100 в 5 минут допустимо
  • Коммерческие издательства — 10-15 запросов в час с одного IP
  • Фармацевтические базы — 5-10 запросов в минуту

Реализация rate limiter на Python

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"Rate limit reached, sleeping {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

# Теперь функция автоматически соблюдает rate limit
for i in range(10):
    result = fetch_pubmed_page(f"https://pubmed.ncbi.nlm.nih.gov/?term=test&page={i}")
    print(f"Page {i} fetched")

Адаптивный rate limiting

Более продвинутый подход — адаптивное изменение скорости в зависимости от ответов сервера. Если получаем ошибки 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"Rate limit hit! Increasing delay to {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 — headless-браузеры лучше проходят проверки Cloudflare
  • Используйте библиотеку cloudscraper для Python — она автоматически обходит базовую защиту Cloudflare
  • Включите cookies и JavaScript — Cloudflare проверяет их наличие
  • Добавьте TLS fingerprinting — используйте curl_cffi для имитации настоящего браузера на уровне TLS

Проблема: Получаю ошибку 429 Too Many Requests

Симптомы: После нескольких успешных запросов сервер начинает возвращать 429.

Решение:

  • Увеличьте задержку между запросами — попробуйте начать с 3-5 секунд
  • Включите ротацию IP — каждый запрос через новый IP снимает rate limiting
  • Проверьте заголовок Retry-After в ответе 429 — он указывает, сколько секунд нужно ждать
  • Используйте экспоненциальную задержку при повторах — 1s, 2s, 4s, 8s и т.д.

Проблема: Прокси работают медленно или часто отваливаются

Симптомы: Timeout errors, очень долгая загрузка страниц, обрывы соединения.

Решение:

  • Увеличьте timeout в запросах до 30-60 секунд — резидентные прокси могут быть медленнее
  • Используйте географически близкие прокси — если парсите европейский сайт, используйте европейские IP
  • Проверьте качество провайдера прокси — дешёвые прокси часто нестабильны
  • Добавьте retry logic — автоматически повторяйте запрос при ошибке соединения
  • Используйте connection pooling — переиспользуйте TCP-соединения через requests.Session()

Проблема: Сайт требует авторизацию или подписку

Симптомы: Доступ к полным текстам статей ограничен, требуется логин.

Решение:

  • Используйте институциональный доступ — многие университеты и больницы имеют подписки
  • Проверьте наличие Open Access версий — многие статьи доступны бесплатно через репозитории
  • Используйте API вместо парсинга — некоторые издательства предоставляют API для исследователей
  • Парсите только метаданные (заголовки, авторы, аннотации) — они обычно доступны бесплатно

Проблема: JavaScript-контент не загружается

Симптомы: В HTML нет нужных данных, видны только загрузочные спиннеры или пустые контейнеры.

Решение:

  • Переключитесь на Selenium/Puppeteer — они выполняют JavaScript
  • Найдите API-эндпоинт — откройте DevTools в браузере, вкладка Network, и найдите XHR-запросы с данными
  • Используйте requests-html — библиотека с