Cloudflare bloquea agresivamente los scrapers en 2026, y "bloqueado por Cloudflare durante 3 días" es el hilo principal en r/vibecoding. La solución es detectar temprano la página de desafío de Cloudflare y enrutar las URL afectadas a una API administrada. Este tutorial muestra cómo construir el detector y retroceder limpiamente.
Requisitos previos
- Python 3.8+
- solicita biblioteca
- Una clave API de Scavio (como alternativa)
Guia paso a paso
Paso 1: Identificar firmas de respuesta de Cloudflare
Los desafíos de Cloudflare devuelven un 403 o 503 con marcadores HTML específicos.
CF_MARKERS = [
'cf-browser-verification',
'cf_chl_opt',
'checking your browser',
'ray id'
]Paso 2: Construye el detector
Devuelve True si la respuesta contiene algún marcador de Cloudflare.
def is_cloudflare_blocked(response):
if response.status_code in (403, 503):
return True
body = response.text.lower()
return any(m in body for m in CF_MARKERS)Paso 3: Pruebe primero el raspado directo
Intente una búsqueda directa con un agente de usuario similar a un navegador.
import requests
HEADERS = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'}
def try_direct(url):
r = requests.get(url, headers=HEADERS, timeout=10)
return r if not is_cloudflare_blocked(r) else NonePaso 4: Regresar a Scavio
Cuando esté bloqueado, diríjase a través del extractor administrado de Scavio.
import os
def fallback_scavio(url):
r = requests.post('https://api.scavio.dev/api/v1/extract',
headers={'x-api-key': os.environ['SCAVIO_API_KEY']},
json={'url': url, 'render_js': True, 'bypass_cloudflare': True})
return r.json()Paso 5: Combinar en un buscador resistente
Pruebe directamente, recurra a la detección.
def resilient_fetch(url):
direct = try_direct(url)
if direct:
return {'source': 'direct', 'content': direct.text}
print(f'Cloudflare detected on {url}, falling back to Scavio')
return {'source': 'scavio', 'content': fallback_scavio(url).get('html', '')}Ejemplo en Python
import os, requests
CF_MARKERS = ['cf-browser-verification', 'cf_chl_opt', 'checking your browser', 'ray id']
HEADERS = {'User-Agent': 'Mozilla/5.0'}
def is_cf(response):
return response.status_code in (403, 503) or any(m in response.text.lower() for m in CF_MARKERS)
def fetch(url):
try:
r = requests.get(url, headers=HEADERS, timeout=10)
if not is_cf(r):
return r.text
except Exception:
pass
r = requests.post('https://api.scavio.dev/api/v1/extract',
headers={'x-api-key': os.environ['SCAVIO_API_KEY']},
json={'url': url, 'render_js': True, 'bypass_cloudflare': True})
return r.json().get('html', '')
print(fetch('https://example-cloudflare-site.com')[:200])Ejemplo en JavaScript
const CF_MARKERS = ['cf-browser-verification', 'cf_chl_opt', 'checking your browser', 'ray id'];
async function fetchResilient(url) {
try {
const r = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
const body = (await r.text()).toLowerCase();
if (r.ok && !CF_MARKERS.some(m => body.includes(m))) return body;
} catch {}
const r = await fetch('https://api.scavio.dev/api/v1/extract', {
method: 'POST',
headers: { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ url, render_js: true, bypass_cloudflare: true })
});
return (await r.json()).html;
}
console.log(await fetchResilient('https://example-cloudflare-site.com'));Salida esperada
When the target URL is not blocked, the direct fetch succeeds and returns the HTML. When blocked, the detector prints a Cloudflare alert and Scavio's extractor returns the page content anyway.