Qué hacer si los proxies se banean constantemente: guía completa de diagnóstico y solución de problemas
Los bloqueos constantes de proxies son uno de los problemas más frecuentes al hacer web scraping, automatización y trabajar con múltiples cuentas. En este artículo analizaremos por qué sucede esto y cómo resolver el problema de manera sistemática, en lugar de cambiar proveedores infinitamente esperando un milagro.
Por qué realmente se banean los proxies
Antes de buscar soluciones, necesitas entender la mecánica de los bloqueos. Los sistemas antifraude modernos utilizan protección multinivel, y el bloqueo de proxies es solo una consecuencia, no la causa. Comprender cómo funcionan estos sistemas te permite construir una estrategia efectiva de evasión.
Reputación de IP y listas negras
Cada dirección IP tiene una reputación que se forma basándose en el historial de su uso. Si una dirección se utilizó anteriormente para spam, ataques DDoS o web scraping masivo, termina en bases de datos como Spamhaus, SORBS o listas propietarias de servicios específicos. Cuando te conectas a través de tal IP, el sistema inmediatamente te trata con sospecha.
Los proxies de centros de datos son especialmente propensos a este problema. Subredes completas pueden estar marcadas como "hosting", y cualquier tráfico desde ellas recibe automáticamente un nivel de verificación elevado. Amazon AWS, Google Cloud, DigitalOcean — sus rangos de IP son bien conocidos y frecuentemente bloqueados de manera preventiva.
Puedes verificar la reputación de una IP a través de servicios como IPQualityScore, Scamalytics o AbuseIPDB. Si tu proxy muestra una puntuación de fraude superior a 75, el problema está exactamente aquí — cambia de proveedor o tipo de proxy.
Patrones de solicitudes
Un humano no realiza 100 solicitudes por segundo. Un humano no navega por páginas con una periodicidad perfecta de 2 segundos. Un humano no ignora imágenes, CSS y JavaScript, solicitando solo HTML. Los sistemas antifraude analizan exactamente estos patrones, y cualquier desviación del comportamiento "humano" aumenta el riesgo de bloqueo.
Especialmente reveladora es la estadística del tiempo entre solicitudes. Si tienes un intervalo estable — es un signo evidente de automatización. Agregar un retraso aleatorio (por ejemplo, de 1 a 5 segundos) reduce significativamente la probabilidad de detección.
Inconsistencia de metadatos
Cuando tu User-Agent dice que usas Chrome en Windows, pero los encabezados HTTP revelan características de Python requests — es una bandera roja. Cuando la dirección IP se geolocaliza en Alemania, pero la configuración de idioma del navegador indica ruso — otra bandera roja. Cuando la zona horaria en JavaScript no coincide con la geografía de la IP — una tercera bandera.
La acumulación de tales inconsistencias hace que el sistema clasifique la conexión como sospechosa y aplique medidas de protección: desde captchas hasta bloqueo completo de IP.
Huella digital del navegador
Los sistemas de protección modernos recopilan decenas de parámetros del navegador: resolución de pantalla, fuentes instaladas, complementos, renderizado WebGL, contexto de audio y mucho más. El conjunto de estos parámetros crea una "huella" única que permanece constante incluso cuando cambias de IP.
Si cambias de proxy pero la huella digital permanece igual, el sistema entiende que eres el mismo usuario. Y si una huella digital aparece desde cientos de IPs diferentes en poco tiempo — es un signo evidente de automatización.
Diagnóstico: cómo entender la causa del bloqueo
Antes de cambiar configuraciones al azar, realiza un diagnóstico. Esto te ahorrará horas de experimentación y te ayudará a encontrar la causa real del problema. Un enfoque sistemático del diagnóstico es la clave para una solución efectiva.
Paso 1: Verifica el proxy en sí
Comienza con una verificación básica de la funcionalidad del proxy independientemente de tu script principal:
import requests
proxy = {
"http": "http://user:pass@proxy-server:port",
"https": "http://user:pass@proxy-server:port"
}
# Verificación de funcionalidad básica
try:
response = requests.get("https://httpbin.org/ip", proxies=proxy, timeout=10)
print(f"IP a través del proxy: {response.json()['origin']}")
except Exception as e:
print(f"Error de conexión: {e}")
# Verificación de fuga de IP real
response = requests.get("https://browserleaks.com/ip", proxies=proxy)
# Compara con tu IP real
Si el proxy no funciona ni en solicitudes simples — el problema está en el proxy mismo o en las credenciales. Verifica el formato correcto de conexión, la disponibilidad de fondos en la cuenta y los límites del proveedor.
Paso 2: Verifica la reputación de la IP
Usa varios servicios para una evaluación integral:
# Obtén la IP del proxy
proxy_ip = requests.get("https://api.ipify.org", proxies=proxy).text
# Verifica en estos servicios:
# https://www.ipqualityscore.com/free-ip-lookup-proxy-vpn-test
# https://scamalytics.com/ip/{proxy_ip}
# https://www.abuseipdb.com/check/{proxy_ip}
# https://whatismyipaddress.com/ip/{proxy_ip}
print(f"Verifica la IP {proxy_ip} en los servicios anteriores")
Presta atención a los siguientes indicadores: puntuación de fraude (debe ser inferior a 50), tipo de IP (residencial es mejor que centro de datos), presencia en listas negras. Si la IP está marcada como VPN/Proxy — muchos sitios la tratarán con sospecha desde el principio.
Paso 3: Aísla el problema
Intenta el mismo proxy en diferentes sitios objetivo. Si el bloqueo ocurre en todas partes — el problema está en el proxy o tu configuración. Si solo en un sitio específico — el problema está en la protección de ese sitio o tu comportamiento en él.
También intenta diferentes proxies en un sitio. Si todos se bloquean — el problema no está en el proxy, sino en tu script, huella digital o patrón de comportamiento. Esta es una prueba crítica que muchos omiten.
Paso 4: Análisis de respuestas del servidor
Diferentes tipos de bloqueos se manifiestan de diferentes maneras. Aprende a distinguirlos:
def analyze_response(response):
status = response.status_code
if status == 403:
print("Acceso prohibido — posiblemente IP en lista negra")
elif status == 429:
print("Demasiadas solicitudes — reduce la frecuencia")
elif status == 503:
print("Servicio no disponible — posiblemente protección DDoS")
elif status == 407:
print("Se requiere autenticación de proxy — verifica credenciales")
elif "captcha" in response.text.lower():
print("Captcha detectada — sospecha de bot")
elif "blocked" in response.text.lower():
print("Bloqueo explícito — cambia IP y reconsideración el enfoque")
elif len(response.text) < 1000:
print("Respuesta sospechosamente corta — posiblemente un stub")
else:
print(f"Estado {status}, longitud de respuesta: {len(response.text)}")
Rotación correcta: frecuencia, lógica, implementación
La rotación de proxies no es simplemente "cambiar IP más frecuentemente". La rotación incorrecta puede causar más daño que su ausencia. Consideremos diferentes estrategias y cuándo aplicarlas.
Estrategia 1: Rotación por número de solicitudes
El enfoque más simple — cambiar IP después de un cierto número de solicitudes. Adecuado para web scraping donde no se necesita sesión:
import random
class ProxyRotator:
def __init__(self, proxy_list, requests_per_proxy=50):
self.proxies = proxy_list
self.requests_per_proxy = requests_per_proxy
self.current_proxy = None
self.request_count = 0
def get_proxy(self):
if self.current_proxy is None or self.request_count >= self.requests_per_proxy:
# Agregamos aleatoriedad al número de solicitudes
self.requests_per_proxy = random.randint(30, 70)
self.current_proxy = random.choice(self.proxies)
self.request_count = 0
self.request_count += 1
return self.current_proxy
# Uso
rotator = ProxyRotator(proxy_list)
for url in urls_to_scrape:
proxy = rotator.get_proxy()
response = requests.get(url, proxies={"http": proxy, "https": proxy})
Nota la aleatoriedad en el número de solicitudes por proxy. Un número fijo (por ejemplo, exactamente 50) es un patrón que puede ser detectado. Un rango aleatorio hace el comportamiento menos predecible.
Estrategia 2: Rotación por tiempo
Para tareas donde la sesión es importante (por ejemplo, trabajar con cuentas), es mejor vincular la IP al tiempo:
import time
import random
class TimeBasedRotator:
def __init__(self, proxy_list, min_minutes=10, max_minutes=30):
self.proxies = proxy_list
self.min_seconds = min_minutes * 60
self.max_seconds = max_minutes * 60
self.current_proxy = None
self.rotation_time = 0
def get_proxy(self):
current_time = time.time()
if self.current_proxy is None or current_time >= self.rotation_time:
self.current_proxy = random.choice(self.proxies)
# Intervalo aleatorio hasta la próxima rotación
interval = random.randint(self.min_seconds, self.max_seconds)
self.rotation_time = current_time + interval
print(f"Nuevo proxy, próxima rotación en {interval//60} minutos")
return self.current_proxy
Estrategia 3: Sesiones pegajosas para cuentas
Al trabajar con múltiples cuentas, es crítico que cada cuenta use una IP constante. Cambiar IP para una cuenta conectada es el camino seguro al bloqueo:
class AccountProxyManager:
def __init__(self, proxy_pool):
self.proxy_pool = proxy_pool
self.account_proxies = {} # account_id -> proxy
self.used_proxies = set()
def get_proxy_for_account(self, account_id):
# Si la cuenta ya tiene un proxy asignado — lo devolvemos
if account_id in self.account_proxies:
return self.account_proxies[account_id]
# Encontramos un proxy libre
available = [p for p in self.proxy_pool if p not in self.used_proxies]
if not available:
raise Exception("No hay proxies libres para nuevas cuentas")
proxy = random.choice(available)
self.account_proxies[account_id] = proxy
self.used_proxies.add(proxy)
return proxy
def release_account(self, account_id):
"""Libera el proxy al eliminar la cuenta"""
if account_id in self.account_proxies:
proxy = self.account_proxies.pop(account_id)
self.used_proxies.discard(proxy)
# Uso
manager = AccountProxyManager(residential_proxy_list)
for account in accounts:
proxy = manager.get_proxy_for_account(account.id)
# Todas las acciones de esta cuenta van a través de una IP
Estrategia 4: Rotación adaptativa
El enfoque más avanzado — cambiar proxy en respuesta a señales del sitio objetivo:
class AdaptiveRotator:
def __init__(self, proxy_list):
self.proxies = proxy_list
self.current_proxy = random.choice(proxy_list)
self.proxy_scores = {p: 100 for p in proxy_list} # "Salud" inicial del proxy
def get_proxy(self):
return self.current_proxy
def report_result(self, success, response_code=200):
"""Se llama después de cada solicitud"""
if success and response_code == 200:
# Solicitud exitosa — aumentamos ligeramente la puntuación
self.proxy_scores[self.current_proxy] = min(100,
self.proxy_scores[self.current_proxy] + 1)
elif response_code == 429:
# Límite de velocidad — reducimos mucho y rotamos
self.proxy_scores[self.current_proxy] -= 30
self._rotate()
elif response_code == 403:
# Bloqueo — ponemos puntuación a cero y rotamos
self.proxy_scores[self.current_proxy] = 0
self._rotate()
elif response_code == 503:
# Posible protección — reducimos y rotamos
self.proxy_scores[self.current_proxy] -= 20
self._rotate()
def _rotate(self):
# Seleccionamos proxy con mejor puntuación
available = [(p, s) for p, s in self.proxy_scores.items() if s > 20]
if not available:
# Todos los proxies están "muertos" — reiniciamos puntuaciones
self.proxy_scores = {p: 50 for p in self.proxies}
available = list(self.proxy_scores.items())
# Selección ponderada por puntuación
self.current_proxy = max(available, key=lambda x: x[1])[0]
print(f"Rotación a proxy con puntuación {self.proxy_scores[self.current_proxy]}")
Huella digital del navegador y su papel en los bloqueos
La huella digital es el conjunto de características del navegador que permite identificar al usuario incluso sin cookies. Si cambias de IP pero la huella digital permanece igual, el sistema de protección fácilmente vincula todas tus sesiones.
De qué se compone la huella digital
La huella digital moderna incluye decenas de parámetros. Aquí están las categorías principales:
| Categoría | Parámetros | Peso en identificación |
|---|---|---|
| User-Agent | Navegador, versión, SO | Medio |
| Pantalla | Resolución, profundidad de color, pixel ratio | Medio |
| Fuentes | Lista de fuentes instaladas | Alto |
| WebGL | Renderer, vendor, hash de renderizado | Muy alto |
| Canvas | Hash de imagen dibujada | Muy alto |
| Audio | Huella de AudioContext | Alto |
| Zona horaria | Zona horaria, offset | Medio |
| Idiomas | navigator.languages | Medio |
| Complementos | navigator.plugins | Bajo (en navegadores modernos) |
Consistencia entre huella digital e IP
Es crítico que la huella digital corresponda a la geografía de la IP. Si el proxy está en Alemania, la huella digital debe parecer un usuario alemán:
// Ejemplo de inconsistencia (MALO):
// IP: Alemania
// Zona horaria: America/New_York
// Idiomas: ["ru-RU", "ru"]
// Esto causará sospecha
// Huella digital consistente (BUENO):
// IP: Alemania
// Zona horaria: Europe/Berlin
// Idiomas: ["de-DE", "de", "en-US", "en"]
Herramientas para gestionar la huella digital
Para trabajo serio, usa herramientas especializadas:
Playwright con Stealth:
from playwright.sync_api import sync_playwright
from playwright_stealth import stealth_sync
with sync_playwright() as p:
browser = p.chromium.launch(
proxy={"server": "http://proxy:port", "username": "user", "password": "pass"}
)
context = browser.new_context(
viewport={"width": 1920, "height": 1080},
locale="de-DE",
timezone_id="Europe/Berlin",
geolocation={"latitude": 52.52, "longitude": 13.405},
permissions=["geolocation"]
)
page = context.new_page()
stealth_sync(page) # Aplicamos parches stealth
page.goto("https://target-site.com")
Puppeteer con puppeteer-extra:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
const browser = await puppeteer.launch({
args: [`--proxy-server=http://proxy:port`]
});
const page = await browser.newPage();
// Redefinimos la zona horaria
await page.evaluateOnNewDocument(() => {
Object.defineProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', {
value: function() {
return { timeZone: 'Europe/Berlin' };
}
});
});
Navegadores anti-detección
Para trabajar con cuentas, a menudo se usan navegadores anti-detección (Multilogin, GoLogin, Dolphin Anty y otros). Permiten crear perfiles aislados con huella digital única. Cada perfil tiene su propio conjunto de parámetros, cookies, localStorage — un entorno completamente aislado.
La ventaja de los anti-detectores es que resuelven el problema de huella digital "de fábrica". La desventaja es el costo y la complejidad de automatización (aunque muchos tienen API).
Patrones de comportamiento: cómo no parecer un bot
Incluso con huella digital perfecta e IP limpia, puedes recibir un bloqueo por comportamiento no humano. Los sistemas modernos analizan no solo parámetros técnicos, sino también patrones de interacción con el sitio.
Retrasos temporales
Un humano no realiza solicitudes con intervalo constante. Agrega retrasos aleatorios con distribución normal:
import random
import time
import numpy as np
def human_delay(min_sec=1, max_sec=5, mean=2.5):
"""
Genera un retraso similar al humano.
Usa distribución lognormal —
la mayoría de retrasos son cortos, pero a veces hay retrasos largos.
"""
delay = np.random.lognormal(mean=np.log(mean), sigma=0.5)
delay = max(min_sec, min(max_sec, delay))
return delay
def human_typing_delay():
"""Retraso entre pulsaciones de teclas al escribir texto"""
return random.uniform(0.05, 0.25)
# Uso
for url in urls:
response = requests.get(url, proxies=proxy)
process(response)
time.sleep(human_delay()) # Pausa aleatoria entre solicitudes
Imitación de navegación
Un humano no va directamente a la página del producto por un enlace directo. Entra en la página principal, usa búsqueda, navega por categorías. Imita este camino:
async def human_like_navigation(page, target_url):
"""Imita navegación humana hacia la página objetivo"""
# 1. Vamos a la página principal
await page.goto("https://example.com")
await page.wait_for_timeout(random.randint(2000, 4000))
# 2. A veces desplazamos la página principal
if random.random() > 0.5:
await page.evaluate("window.scrollBy(0, 300)")
await page.wait_for_timeout(random.randint(1000, 2000))
# 3. Usamos búsqueda o navegación
if random.random() > 0.3:
search_box = await page.query_selector('input[type="search"]')
if search_box:
await search_box.type("search query", delay=100)
await page.keyboard.press("Enter")
await page.wait_for_timeout(random.randint(2000, 4000))
# 4. Vamos a la página objetivo
await page.goto(target_url)
# 5. Desplazamos la página como un humano
await human_scroll(page)
async def human_scroll(page):
"""Imita desplazamiento humano"""
scroll_height = await page.evaluate("document.body.scrollHeight")
current_position = 0
while current_position < scroll_height * 0.7: # No hasta el final
scroll_amount = random.randint(200, 500)
await page.evaluate(f"window.scrollBy(0, {scroll_amount})")
current_position += scroll_amount
await page.wait_for_timeout(random.randint(500, 1500))
Movimientos del ratón
Algunos sistemas rastrean movimientos del ratón. El movimiento lineal del punto A al punto B es un signo de bot. Un humano mueve el ratón en una curva con micro-correcciones:
import bezier
import numpy as np
def generate_human_mouse_path(start, end, num_points=50):
"""
Genera un camino de ratón similar al humano,
usando curvas de Bezier con pequeño ruido.
"""
# Puntos de control para la curva de Bezier
control1 = (
start[0] + (end[0] - start[0]) * random.uniform(0.2, 0.4) + random.randint(-50, 50),
start[1] + (end[1] - start[1]) * random.uniform(0.2, 0.4) + random.randint(-50, 50)
)
control2 = (
start[0] + (end[0] - start[0]) * random.uniform(0.6, 0.8) + random.randint(-50, 50),
start[1] + (end[1] - start[1]) * random.uniform(0.6, 0.8) + random.randint(-50, 50)
)
# Creamos la curva de Bezier
nodes = np.asfortranarray([
[start[0], control1[0], control2[0], end[0]],
[start[1], control1[1], control2[1], end[1]]
])
curve = bezier.Curve(nodes, degree=3)
# Generamos puntos en la curva
points = []
for t in np.linspace(0, 1, num_points):
point = curve.evaluate(t)
# Agregamos micro-ruido
x = point[0][0] + random.uniform(-2, 2)
y = point[1][0] + random.uniform(-2, 2)
points.append((x, y))
return points
async def human_click(page, selector):
"""Hace clic en elemento con movimiento de ratón similar al humano"""
element = await page.query_selector(selector)
box = await element.bounding_box()
# Punto objetivo — no el centro, sino un punto aleatorio dentro del elemento
target_x = box['x'] + random.uniform(box['width'] * 0.2, box['width'] * 0.8)
target_y = box['y'] + random.uniform(box['height'] * 0.2, box['height'] * 0.8)
# Posición actual del ratón (o punto de inicio aleatorio)
start_x = random.randint(0, 1920)
start_y = random.randint(0, 1080)
# Generamos el camino
path = generate_human_mouse_path((start_x, start_y), (target_x, target_y))
# Movemos el ratón por el camino
for x, y in path:
await page.mouse.move(x, y)
await page.wait_for_timeout(random.randint(5, 20))
# Pequeña pausa antes del clic
await page.wait_for_timeout(random.randint(50, 150))
await page.mouse.click(target_x, target_y)
Carga de recursos
Un navegador real carga no solo HTML, sino también CSS, JavaScript, imágenes, fuentes. Si usas requests y solicitas solo HTML — es sospechoso. Al trabajar con navegadores headless, este problema se resuelve automáticamente, pero al usar clientes HTTP, necesitas considerar este aspecto.
Selección del tipo de proxy para tu tarea
Diferentes tipos de proxies tienen diferentes características y son adecuados para diferentes tareas. La selección incorrecta es una causa frecuente de bloqueos.
Proxies de centro de datos
Los proxies de centro de datos son direcciones IP que pertenecen a proveedores de hosting. Se pueden identificar fácilmente por pertenencia a AS (sistemas autónomos) de grandes centros de datos.
Ventajas:
- Alta velocidad y estabilidad
- Bajo costo
- Grandes pools de IP
Desventajas:
- Fáciles de detectar
- Frecuentemente en listas negras
- No adecuados para sitios con protección seria
Adecuados para: herramientas SEO, verificación de disponibilidad, trabajo con API sin protección estricta, pruebas.
Proxies residenciales
Los proxies residenciales son direcciones IP de usuarios reales, proporcionadas a través de programas de asociados o SDK en aplicaciones. Pertenecen a proveedores de Internet ordinarios (ISP).
Ventajas:
- Parecen usuarios ordinarios
- Puntuación de fraude baja
- Geografía amplia
- Difíciles de detectar
Desventajas:
- Costo más alto (pago por tráfico)
- La velocidad depende del usuario final
- Las IP pueden "desaparecer" (el usuario apagó el dispositivo)
Adecuados para: web scraping de sitios protegidos, trabajo con redes sociales, e-commerce, cualquier tarea donde es importante no ser detectado.
Proxies móviles
Los proxies móviles son direcciones IP de operadores móviles (MTS, Beeline, Megafon y análogos en otros países). Tienen un estado especial debido a la tecnología CGNAT.
Ventajas:
- Máxima confianza de los sitios
- Una IP la usan miles de usuarios reales — difícil de banear
- Ideales para trabajar con cuentas
- Cambio de IP por solicitud (reconexión a la red)
Desventajas:
- El costo más alto
- Velocidad limitada
- Menos opciones de geografía
Adecuados para: multi-accounting, trabajo con Instagram/Facebook/TikTok, registro de cuentas, cualquier tarea con alto riesgo de bloqueo.
Tabla comparativa
| Parámetro | Centro de datos | Residenciales | Móviles |
|---|---|---|---|
| Detectabilidad | Alta | Baja | Muy baja |
| Velocidad | Alta | Media | Baja-media |
| Costo | $ | $$ | $$$ |
| Para redes sociales | No adecuados | Adecuados | Ideales |
| Para web scraping | Sitios simples | Cualquier sitio | Excesivo |
Técnicas avanzadas de evasión de protección
Cuando los métodos básicos no funcionan, es necesario usar técnicas más complejas. Consideremos varios enfoques avanzados.
Trabajo con Cloudflare y protecciones similares
Cloudflare, Akamai, PerimeterX — estos sistemas usan desafíos JavaScript para verificar el navegador. Una solicitud HTTP simple no pasará. Opciones de solución:
1. Uso de navegador real:
from playwright.sync_api import sync_playwright
def bypass_cloudflare(url, proxy):
with sync_playwright() as p:
browser = p.chromium.launch(
headless=False, # A veces headless se detecta
proxy={"server": proxy}
)
page = browser.new_page()
page.goto(url)
# Esperamos el paso de la verificación (normalmente 5-10 segundos)
page.wait_for_timeout(10000)
# Verificamos si pasamos
if "challenge" not in page.url:
# Guardamos cookies para solicitudes posteriores
cookies = page.context.cookies()
return cookies
browser.close()
return None
2. Uso de soluciones listas:
# cloudscraper — biblioteca para evadir Cloudflare
import cloudscraper
scraper = cloudscraper.create_scraper(
browser={
'browser': 'chrome',
'platform': 'windows',
'desktop': True
}
)
scraper.proxies = {"http": proxy, "https": proxy}
response = scraper.get("https://protected-site.com")
Solución de captchas
Si el sitio muestra una captcha, hay varios enfoques:
Servicios de reconocimiento: 2Captcha, Anti-Captcha, CapMonster. Resuelven captchas por ti