Torna al blog

Proxy per il parsing dei dati medici: come raccogliere informazioni senza blocchi

Scopri come estrarre in modo sicuro dati medici da studi clinici, banche dati di farmaci e riviste mediche senza subire blocchi.

📅9 marzo 2026
```html

Il parsing dei dati medici è un compito che richiede un approccio particolare nella scelta dei proxy. I portali medici, i database di studi clinici e le risorse farmaceutiche utilizzano sistemi avanzati di protezione contro la raccolta automatizzata di dati. In questo articolo esamineremo come configurare correttamente i proxy per un parsing sicuro delle informazioni mediche, evitare i blocchi e raccogliere i dati necessari in modo efficace.

Perché i siti medici bloccano il parsing

I portali medici e i database sono particolarmente sensibili alla raccolta automatizzata di informazioni per diverse ragioni. In primo luogo, molti di essi operano su base commerciale e vendono l'accesso ai dati tramite abbonamenti a pagamento. Il parsing automatico può violare i termini di utilizzo e i contratti di licenza.

In secondo luogo, i dati medici contengono spesso informazioni riservate protette dalla legge (HIPAA negli Stati Uniti, GDPR in Europa). I proprietari delle risorse sono obbligati a controllare l'accesso a tali dati e prevenire la loro diffusione non autorizzata. Pertanto, utilizzano sistemi di protezione avanzati:

  • Rate limiting — limitazione del numero di richieste da un singolo indirizzo IP in un'unità di tempo (di solito 10-50 richieste al minuto)
  • Fingerprinting — analisi delle caratteristiche del browser, delle intestazioni HTTP, dell'ordine di caricamento delle risorse
  • CAPTCHA — sistemi come reCAPTCHA v3, che si attivano in caso di attività sospette
  • Blocco IP — blocco temporaneo o permanente degli indirizzi IP dei data center
  • Cloudflare e simili — protezione contro i bot a livello CDN

La terza ragione è il carico sui server. I database medici contengono spesso milioni di record, e il parsing di massa può creare un carico significativo sull'infrastruttura. Pertanto, gli amministratori combattono attivamente la raccolta automatizzata di dati, monitorando i modelli di comportamento tipici dei bot: intervalli identici tra le richieste, navigazione lineare delle pagine, assenza di JavaScript e cookie.

Importante: Prima di iniziare il parsing dei dati medici, assicurati di studiare i termini di utilizzo del sito e la legislazione applicabile. Alcuni dati possono essere protetti da copyright o contenere informazioni personali sui pazienti. Assicurati che la tua attività sia legale e non violi i diritti di terzi.

Quale tipo di proxy scegliere per i dati medici

La scelta del tipo di proxy è fondamentale per un parsing di successo dei dati medici. Diverse fonti richiedono approcci diversi. Esaminiamo i principali tipi di proxy e la loro applicabilità:

Tipo di proxy Vantaggi Svantaggi Quando utilizzare
Proxy dei data center Alta velocità (100+ Mbps), basso costo, connessione stabile Facilmente rilevabili, spesso bloccati su siti protetti Database aperti senza protezione rigorosa (PubMed, WHO)
Proxy residenziali IP reali di utenti domestici, basso rischio di blocco, superano Cloudflare Costo più elevato, velocità variabile, possono essere instabili Database commerciali protetti (Elsevier, Springer), siti con Cloudflare
Proxy mobili Massimo livello di fiducia (IP degli operatori mobili), praticamente non bloccati I più costosi, geografia limitata, possono essere più lenti Risorse particolarmente protette, quando i proxy residenziali non funzionano
Proxy ISP Velocità dei data center + fiducia dei residenziali, IP statici Costo medio, disponibilità limitata Parsing a lungo termine da un IP, quando è necessaria stabilità

Per la maggior parte delle attività di parsing dei dati medici, si raccomanda di utilizzare proxy residenziali. Offrono un equilibrio ottimale tra costo ed efficienza. I proxy dei data center sono adatti solo per fonti aperte senza protezione. I proxy mobili dovrebbero essere utilizzati solo in casi estremi, quando altri tipi non funzionano.

Raccomandazioni per la scelta per fonti specifiche

  • PubMed, PubMed Central — i proxy dei data center sono sufficienti, ma con limitazione della velocità a 3 richieste al secondo
  • ClinicalTrials.gov — proxy dei data center, c'è un'API ufficiale
  • Elsevier, Springer, Wiley — i proxy residenziali sono obbligatori, utilizzano fingerprinting avanzato
  • DrugBank, RxList — proxy residenziali, protezione attiva contro il parsing
  • Database FDA, EMA — i proxy dei data center sono adatti, ma con velocità di parsing lenta

Principali fonti di dati medici e la loro protezione

I dati medici sono distribuiti su molte fonti, ciascuna con la propria specificità e livello di protezione. Comprendere queste caratteristiche aiuterà a configurare correttamente la strategia di parsing.

Database pubblici governativi

PubMed/PubMed Central — il più grande database di pubblicazioni mediche, contiene oltre 35 milioni di record. La Biblioteca Nazionale di Medicina degli Stati Uniti (NLM) fornisce un'API ufficiale E-utilities, che è il modo preferito per accedere ai dati. Il parsing diretto dell'interfaccia web è possibile, ma limitato a 3 richieste al secondo da un singolo IP. Superare il limite porta a un blocco temporaneo di 24 ore.

ClinicalTrials.gov — database di studi clinici, contiene informazioni su oltre 400.000 studi in 220 paesi. Fornisce anche un'API per l'accesso programmato. L'interfaccia web è protetta da rate limiting — massimo 100 richieste in 5 minuti da un singolo IP. Utilizza una protezione di base contro i bot, ma senza Cloudflare.

FDA Drugs Database — database dei farmaci approvati dalla FDA. Accesso aperto tramite interfaccia web e API openFDA. Limitazioni: 240 richieste al minuto per utenti anonimi, 1000 richieste al minuto con chiave API. I blocchi sono rari, ma possibili con un parsing aggressivo.

Editori scientifici commerciali

Elsevier (ScienceDirect) — uno dei maggiori editori di letteratura scientifica. Utilizza una protezione multilivello: Cloudflare, fingerprinting del browser, analisi del comportamento dell'utente. Rileva modelli di download automatico: accesso sequenziale agli articoli, assenza di JavaScript, User-Agent non tipici. Quando viene rilevato il parsing, blocca l'IP a livello di account e può bloccare l'intero istituto. È obbligatorio utilizzare proxy residenziali con rotazione e completa emulazione del browser.

Springer Nature — protezione simile, monitora anche la velocità di scorrimento delle pagine e il movimento del mouse. Utilizza il machine learning per la rilevazione dei bot. Si raccomanda di eseguire il parsing di non più di 10-15 articoli all'ora da un singolo IP, con ritardi randomizzati tra le richieste.

Wiley Online Library — protezione meno aggressiva, ma richiede comunque l'uso di proxy. Consente circa 50 richieste all'ora da un singolo IP senza blocco. Utilizza cookie di sessione per monitorare l'attività.

Database farmaceutici

DrugBank — database completo di farmaci. La versione gratuita è limitata all'interfaccia web, mentre la versione commerciale fornisce API e download di dati. La versione web è protetta da Cloudflare e rate limiting — massimo 20 richieste al minuto. Rileva l'automazione per assenza di cookie e JavaScript.

RxList, Drugs.com — guide popolari sui farmaci per i consumatori. Utilizzano Cloudflare e combattono attivamente il parsing. Bloccano gli IP dei data center praticamente istantaneamente. Sono necessari proxy residenziali e una velocità di parsing lenta (5-10 pagine al minuto).

Configurazione della rotazione IP per il parsing a lungo termine

La corretta rotazione degli indirizzi IP è un fattore chiave per il successo del parsing dei dati medici. Ci sono due approcci principali: rotazione a livello di richieste e rotazione temporale.

Rotazione a livello di richieste

Con questo approccio, ogni richiesta viene inviata tramite un nuovo indirizzo IP. Questo riduce al minimo il rischio di blocco, ma può causare problemi con i siti che monitorano le sessioni tramite cookie. È adatto per il parsing di elenchi e cataloghi, dove non è necessario mantenere lo stato della sessione.

La maggior parte dei fornitori di proxy residenziali offre rotazione automatica tramite un endpoint speciale. Ad esempio, utilizzando un endpoint di proxy rotante, ogni nuova connessione TCP riceve un nuovo IP. Questo funziona automaticamente con librerie come requests in Python, poiché per impostazione predefinita viene creata una nuova connessione per ogni richiesta.

Rotazione temporale (sticky sessions)

Le sticky sessions consentono di utilizzare un indirizzo IP per un periodo di tempo specifico (di solito 5-30 minuti), dopo di che avviene il cambio automatico. Questo è utile per i siti che richiedono autenticazione o monitorano lo stato della sessione tramite cookie. Puoi eseguire il parsing di più pagine da un singolo IP, imitando il comportamento di un utente reale, e poi l'IP cambia automaticamente.

Per i siti medici, si raccomanda di utilizzare sticky sessions della durata di 10-15 minuti. Durante questo tempo, puoi eseguire il parsing di 10-20 pagine (a seconda dei ritardi), dopo di che l'IP cambia e inizi una "nuova sessione". Questo appare naturale e riduce il rischio di rilevamento.

Dimensione del pool di indirizzi IP

Per il parsing a lungo termine, è importante la dimensione del pool di indirizzi IP disponibili. Se utilizzi lo stesso insieme di 100 IP per una settimana, il sito potrebbe notare un modello e bloccare tutti questi indirizzi. I proxy residenziali di solito forniscono accesso a milioni di IP, il che praticamente esclude il riutilizzo dello stesso indirizzo.

Quando si utilizzano proxy dei data center, si consiglia di avere un pool di almeno 500-1000 IP per il parsing di un volume medio (10.000-50.000 pagine al mese). Per il parsing su larga scala (centinaia di migliaia di pagine), è meglio utilizzare proxy residenziali con i loro enormi pool di IP.

Consiglio sulla rotazione per diverse fonti:

  • PubMed — la rotazione non è obbligatoria, basta 1 IP rispettando il rate limit
  • Editori commerciali — sticky sessions di 10-15 minuti, nuovo IP ogni 15-20 pagine
  • Database farmaceutici — rotazione per ogni richiesta o sticky sessions di 5 minuti
  • Siti con Cloudflare — sticky sessions obbligatorie, rotazione a livello di richieste non funziona

Esempi di codice in Python per il parsing con proxy

Esaminiamo esempi pratici di configurazione dei proxy per il parsing dei dati medici utilizzando librerie Python popolari. Iniziamo con un esempio di base e lo complessiamo gradualmente.

Configurazione di base con la libreria requests

import requests
from time import sleep
import random

# Configurazione del proxy (sostituisci con i tuoi dati)
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}'
}

# Intestazioni per imitare un browser reale
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'
}

# Esempio di richiesta a PubMed
url = "https://pubmed.ncbi.nlm.nih.gov/?term=diabetes"

try:
    response = requests.get(url, proxies=proxies, headers=headers, timeout=30)
    print(f"Codice di stato: {response.status_code}")
    print(f"Lunghezza del contenuto: {len(response.content)}")
    
    # Aggiungi un ritardo tra le richieste (obbligatorio per PubMed)
    sleep(random.uniform(1.0, 3.0))
    
except requests.exceptions.RequestException as e:
    print(f"Errore: {e}")

Configurazione avanzata con rotazione 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: lista di dizionari con proxy
        [{'http': 'http://user:pass@host:port', 'https': '...'}, ...]
        """
        self.proxy_list = proxy_list
        self.current_index = 0
    
    def get_next_proxy(self):
        """Ottieni il prossimo proxy dalla lista"""
        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():
    """Crea una sessione con ripetizioni automatiche in caso di errori"""
    session = requests.Session()
    
    # Configurazione delle ripetizioni automatiche
    retry_strategy = Retry(
        total=3,  # massimo 3 tentativi
        backoff_factor=1,  # ritardo tra i tentativi: 1, 2, 4 secondi
        status_forcelist=[429, 500, 502, 503, 504],  # codici per ripetere
        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):
    """Parsing di un elenco di URL con rotazione dei proxy"""
    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:
        # Otteniamo un nuovo proxy per ogni richiesta
        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"✓ Successo: {url}")
            else:
                results.append({
                    'url': url,
                    'status': 'failed',
                    'error': f"Codice di stato: {response.status_code}"
                })
                print(f"✗ Fallito: {url} (Stato: {response.status_code})")
        
        except requests.exceptions.RequestException as e:
            results.append({
                'url': url,
                'status': 'error',
                'error': str(e)
            })
            print(f"✗ Errore: {url} ({e})")
        
        # Ritardo casuale tra le richieste (importante!)
        sleep(random.uniform(2.0, 5.0))
    
    return results

# Esempio di utilizzo
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)

Utilizzo di Selenium per siti con JavaScript

Molti siti medici moderni utilizzano JavaScript per caricare contenuti. In questi casi è necessario un browser 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):
    """Crea un Chrome WebDriver con proxy"""
    
    chrome_options = Options()
    
    # Modalità headless (senza GUI)
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--disable-dev-shm-usage')
    
    # Configurazione del proxy
    chrome_options.add_argument(f'--proxy-server=http://{proxy_host}:{proxy_port}')
    
    # Disabilita l'automazione (importante per bypassare il rilevamento)
    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)
    
    # Per proxy con autenticazione è necessario utilizzare un'estensione
    # o configurare tramite capabilities (opzione più complessa)
    
    return driver

def scrape_with_selenium(url, driver):
    """Parsing di una pagina con attesa per il caricamento di JavaScript"""
    
    driver.get(url)
    
    # Attesa per il caricamento di un elemento (ad esempio, risultati di ricerca)
    try:
        wait = WebDriverWait(driver, 10)
        results = wait.until(
            EC.presence_of_element_located((By.CLASS_NAME, "results-article"))
        )
        
        # Estrazione dei dati
        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"Errore in attesa degli elementi: {e}")
        return []

# Esempio di utilizzo
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"Titolo: {result['title']}")
        print(f"Autori: {result['authors']}\n")
        
finally:
    driver.quit()

Controllo della velocità delle richieste e bypass del rate limiting

Il rate limiting è una delle principali protezioni dei siti medici contro il parsing. Una corretta configurazione della velocità delle richieste è fondamentale per un parsing a lungo termine senza blocchi.

Determinazione della velocità sicura

Il primo passo è determinare i limiti di un sito specifico. Questo può essere fatto sperimentalmente, aumentando gradualmente la velocità delle richieste fino a quando non si verificano errori 429 (Too Many Requests) o blocchi. Per la maggior parte dei siti medici, i valori sicuri sono:

  • PubMed — massimo 3 richieste al secondo (raccomandazione ufficiale)
  • ClinicalTrials.gov — 20 richieste al minuto sono sicure, fino a 100 in 5 minuti sono accettabili
  • Editori commerciali — 10-15 richieste all'ora da un singolo IP
  • Database farmaceutici — 5-10 richieste al minuto

Implementazione di un rate limiter in Python

import time
from collections import deque

class RateLimiter:
    def __init__(self, max_calls, period):
        """
        max_calls: numero massimo di chiamate
        period: periodo di tempo in secondi
        Ad esempio: RateLimiter(3, 1) = 3 richieste al secondo
        """
        self.max_calls = max_calls
        self.period = period
        self.calls = deque()
    
    def __call__(self, func):
        """Decoratore per limitare la velocità di chiamata della funzione"""
        def wrapper(*args, **kwargs):
            now = time.time()
            
            # Rimuoviamo le chiamate vecchie al di fuori del periodo
            while self.calls and self.calls[0] < now - self.period:
                self.calls.popleft()
            
            # Se abbiamo raggiunto il limite, aspettiamo
            if len(self.calls) >= self.max_calls:
                sleep_time = self.period - (now - self.calls[0])
                if sleep_time > 0:
                    print(f"Limite di velocità raggiunto, attesa {sleep_time:.2f}s")
                    time.sleep(sleep_time)
                    # Pulisci dopo l'attesa
                    self.calls.clear()
            
            # Registriamo il tempo della chiamata
            self.calls.append(time.time())
            
            # Eseguiamo la funzione
            return func(*args, **kwargs)
        
        return wrapper

# Esempio di utilizzo
@RateLimiter(max_calls=3, period=1)  # 3 richieste al secondo
def fetch_pubmed_page(url):
    response = requests.get(url, headers=headers, proxies=proxies)
    return response

# Ora la funzione rispetta automaticamente il rate limit
for i in range(10):
    result = fetch_pubmed_page(f"https://pubmed.ncbi.nlm.nih.gov/?term=test&page={i}")
    print(f"Pagina {i} recuperata")

Rate limiting adattivo

Un approccio più avanzato è modificare la velocità in base alle risposte del server. Se riceviamo errori 429 o 503, riduciamo automaticamente la velocità:

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):
        """Attesa prima della prossima richiesta"""
        # Aggiungiamo casualità per naturalezza
        actual_delay = self.current_delay * random.uniform(0.8, 1.2)
        time.sleep(actual_delay)
    
    def on_success(self):
        """Chiamato quando la richiesta ha successo"""
        self.success_count += 1
        
        # Dopo 10 richieste di successo, aumentiamo un po'
        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):
        """Chiamato quando si riceve 429 o errori simili"""
        # Dobbiamo raddoppiare il ritardo, ma non oltre il massimo
        self.current_delay = min(
            self.current_delay * 2,
            self.max_delay
        )
        self.success_count = 0
        print(f"Limite di velocità colpito! Aumento del ritardo a {self.current_delay:.2f}s")
    
    def on_error(self):
        """Chiamato in caso di altri errori"""
        # Aumentiamo un po' il ritardo
        self.current_delay = min(
            self.current_delay * 1.5,
            self.max_delay
        )
        self.success_count = 0

# Esempio di utilizzo
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()
            # Elaborazione dei dati
            
        elif response.status_code == 429:
            limiter.on_rate_limit()
            # Ripetere più tardi
            
        else:
            limiter.on_error()
            
    except requests.exceptions.RequestException:
        limiter.on_error()

Intestazioni corrette e User-Agent per i siti medici

I siti medici analizzano le intestazioni HTTP per rilevare i bot. Intestazioni errate o mancanti sono una causa comune di blocchi anche quando si utilizzano proxy di alta qualità.

Intestazioni obbligatorie

Il set minimo di intestazioni che deve essere presente in ogni richiesta:

headers = {
    # User-Agent — deve essere un browser attuale
    '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 — tipi di contenuto accettati dal browser
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
    
    # Accept-Language — lingua dell'utente
    'Accept-Language': 'en-US,en;q=0.9',
    
    # Accept-Encoding — supporto per la compressione
    'Accept-Encoding': 'gzip, deflate, br',
    
    # Connection — mantenimento della connessione
    'Connection': 'keep-alive',
    
    # Upgrade-Insecure-Requests — passaggio automatico a HTTPS
    'Upgrade-Insecure-Requests': '1',
    
    # DNT — Do Not Track (opzionale, ma aggiunge realismo)
    'DNT': '1',
    
    # Intestazioni Sec-Fetch-* (importanti per i browser moderni)
    'Sec-Fetch-Dest': 'document',
    'Sec-Fetch-Mode': 'navigate',
    'Sec-Fetch-Site': 'none',
    'Sec-Fetch-User': '?1',
    
    # Cache-Control
    'Cache-Control': 'max-age=0'
}

Rotazione User-Agent

L'utilizzo dello stesso User-Agent può essere sospetto. Si raccomanda di ruotare tra diversi browser attuali:

import random

USER_AGENTS = [
    # Chrome su 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 su 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 su Windows
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
    
    # Firefox su Mac
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0',
    
    # Safari su 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 su 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():
    """Ottieni intestazioni con User-Agent casuale"""
    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'
    }

# Utilizzo
for url in urls:
    headers = get_random_headers()
    response = requests.get(url, headers=headers, proxies=proxies)

Referer e Origin per i moduli

Quando si lavora con moduli di ricerca o si inviano richieste POST, è fondamentale aggiungere le intestazioni Referer e Origin:

# Per richieste POST al modulo di ricerca
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'
}

# Richiesta POST con dati del modulo
data = {
    'query': 'diabetes',
    'page': '1'
}

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

Problemi comuni e loro soluzione

Durante il parsing dei dati medici possono sorgere problemi specifici. Esaminiamo i più comuni e i modi per risolverli.

Problema: Cloudflare blocca tutte le richieste

Sintomi: Ricevi una pagina con il testo "Controllo del browser" o un errore 403 Forbidden con riferimento a Cloudflare.

Soluzione:

  • Utilizza proxy residenziali invece dei data center — Cloudflare blocca gli IP dei data center per impostazione predefinita
  • Passa a Selenium o Puppeteer — i browser headless superano meglio i controlli di Cloudflare
  • Utilizza la libreria cloudscraper per Python — bypassa automaticamente la protezione di base di Cloudflare
  • Attiva i cookie e JavaScript — Cloudflare verifica la loro presenza
  • Aggiungi fingerprinting TLS — utilizza curl_cffi per imitare un vero browser a livello TLS

Problema: Ricevo errore 429 Too Many Requests

Sintomi: Dopo alcune richieste riuscite, il server inizia a restituire 429.

Soluzione:

  • Aumenta il ritardo tra le richieste — prova a partire da 3-5 secondi
  • Attiva la rotazione IP — ogni richiesta tramite un nuovo IP rimuove il rate limiting
  • Controlla l'intestazione Retry-After nella risposta 429 — indica quanti secondi è necessario attendere
  • Utilizza un ritardo esponenziale nei ripetizioni — 1s, 2s, 4s, 8s, ecc.

Problema: I proxy funzionano lentamente o cadono frequentemente

Sintomi: Timeout errors, caricamento molto lento delle pagine, interruzioni della connessione.

Soluzione:

  • Aumenta il timeout nelle richieste a 30-60 secondi — i proxy residenziali possono essere più lenti
  • Utilizza proxy geograficamente vicini — se stai eseguendo il parsing di un sito europeo, utilizza IP europei
  • Controlla la qualità del fornitore di proxy — i proxy economici sono spesso instabili
  • Aggiungi retry logic — ripeti automaticamente la richiesta in caso di errore di connessione
  • Utilizza connection pooling — riutilizza le connessioni TCP tramite requests.Session()

Problema: Il sito richiede autenticazione o abbonamento

Sintomi: L'accesso ai testi completi degli articoli è limitato, è necessario effettuare il login.

Soluzione:

  • Utilizza l'accesso istituzionale — molte università e ospedali hanno abbonamenti
  • Controlla la disponibilità di versioni Open Access — molti articoli sono disponibili gratuitamente tramite repository
  • Utilizza API invece del parsing — alcuni editori forniscono API per i ricercatori
  • Esegui il parsing solo dei metadati (titoli, autori, abstract) — di solito sono disponibili gratuitamente

Problema: Il contenuto JavaScript non viene caricato

Sintomi: Non ci sono dati necessari nell'HTML, si vedono solo spinner di caricamento o contenitori vuoti.

Soluzione:

  • Passa a Selenium/Puppeteer — eseguono JavaScript
  • Trova l'endpoint API — apri DevTools nel browser, scheda Network, e trova le richieste XHR con i dati
  • Utilizza requests-html — libreria con supporto per il rendering JavaScript
```