Resumen
- La rotación de proxies en Python significa elegir una URL de proxy diferente de una lista en cada solicitud, no es un modo especial de la biblioteca
requests.requestssolo sabe cómo usar el diccionario de proxies que le entregues para esa llamada; tu código es quien decide cuál proxy es. - Un diccionario
proxiesindexado por esquema ({"http": ..., "https": ...}) es el único formato querequestsacepta, y se puede pasar por solicitud o establecer una vez en unaSession. Por solicitud es más seguro para la rotación, porque actualizarsession.proxiesen cada iteración del bucle es fácil de errar. - Las variables de entorno silenciosamente sobrescriben
session.proxies. La documentación derequestsadvierte queHTTP_PROXY/HTTPS_PROXYen el entorno tienen prioridad sobre lo que configures en una sesión, y este artículo reproduce esa sobrescritura en tiempo real para que puedas verlo. itertools.cyclete da rotación round-robin en tres líneas, pero no sabe cuándo un proxy está caído. La rotación en producción necesita un bucle de reintento y omisión alrededor de esto, que este artículo construye y ejecuta contra una falla real de proxy.- La clase
Retryde urllib3 reintenta la misma conexión; no rota entre diferentes proxies. Confundir los dos es una razón común por la que el código de rotación "no parece estar rotando" —Retryy la rotación de proxy resuelven problemas diferentes y típicamente necesitas ambos. - Rotar URLs de proxy es independiente de dónde provienen esas URLs. El mismo bucle funciona ya sea que la lista contenga tres IPs que codificaste para prueba o un gran grupo de un proveedor pagado; el código de este artículo fue verificado contra servidores de prueba locales precisamente para que no dependa de ningún proveedor en particular.
Lo que "Proxies Rotativos" Realmente Significa en un Script
Los proxies rotativos significan enviar diferentes solicitudes a través de diferentes servidores proxy de modo que ninguna IP de salida maneje cada solicitud. Nada en Python o requests rota nada automáticamente — requests.get(url, proxies=proxy_dict) envía exactamente una solicitud a través de exactamente un proxy, y es tu bucle el que decide qué proxy_dict pasar en la siguiente llamada. Esa distinción importa porque muchos errores de rotación resultan ser "el bucle nunca cambió realmente el diccionario" en lugar de un problema con el proxy. El resto de este artículo construye ese bucle desde un solo proxy codificado hasta un grupo seguro para hilos con conmutación por error automática, ejecutando cada ejemplo contra servidores locales reales para que puedas ver el comportamiento real en lugar de confiar en un fragmento.
Instalando lo que Necesitas
Necesitas la biblioteca requests, que incluye urllib3 como dependencia y maneja tanto proxies HTTP simples como, desde requests 2.10+, proxies SOCKS si también instalas la opción socks:
pip install requests pip install "requests[socks]" # solo necesario para URLs de proxy socks5://
No se requiere ningún otro paquete para los patrones en este artículo; la rotación es unas pocas docenas de líneas de código de la biblioteca estándar (itertools, random, threading, concurrent.futures) más requests en sí.
Configurando un Solo Proxy Antes de Rotar Cualquier Cosa
Responde al encabezado directamente: requests espera un diccionario con claves "http" y "https", cada una apuntando a una URL de proxy, y pasas ese diccionario a una llamada individual o lo adjuntas a una Session. La documentación de requests muestra la forma por solicitud como:
import requests proxies = { "http": "http://10.10.1.10:3128", "https": "http://10.10.1.10:1080", } requests.get("http://example.org", proxies=proxies)
y la forma por Session como session.proxies.update(proxies) seguido de session.get(...). Las credenciales van directamente en la URL como http://user:pass@host:port. La misma documentación señala un detalle que vale la pena conocer antes de que construyas algo sobre esto: establece claramente que "los valores proporcionados serán sobrescritos por proxies ambientales", lo que significa que una variable de entorno HTTP_PROXY o HTTPS_PROXY tiene prioridad sobre lo que configuras en session.proxies. Eso es real y fácil de alcanzar por accidente: ejecutar lo siguiente en una máquina (o corredor CI) con HTTP_PROXY ya exportado lo reproduce:
import os import requests os.environ["HTTP_PROXY"] = "http://127.0.0.1:8002" session = requests.Session() session.proxies.update({"http": "http://127.0.0.1:8001"}) resp = session
proxy-2
La sesión se indicó que usara el proxy en el puerto 8001; la variable de entorno ganó de todas formas, y cada solicitud en la sesión pasó por el puerto 8002 en su lugar. Si tu código de rotación es una Session y parece estar ignorando tu lista de proxies, verifica tus variables de entorno antes de revisar tu lógica de rotación.
Echa un Vistazo Rápido
Probar la lógica de rotación contra tres servidores locales demuestra que el bucle funciona, pero no te dirá cómo se comportan los proxies reales bajo carga: la puerta de enlace de Nstproxy te brinda una gran piscina de IPs reales a las que apuntar el mismo código una vez que estés listo.
Rotación básica con itertools.cycle
El bucle de rotación más simple recorre una lista fija de URLs de proxy y entrega una nueva a requests en cada llamada. Este ejemplo se ejecutó contra tres servidores HTTP locales iniciados en los puertos 8001-8003 para este artículo, cada uno identificándose en su respuesta por lo que la rotación es verificable en lugar de asumida:
import itertools import requests PROXIES = [ "http://127.0.0.1:8001", "http://127.0.0.1:8002", "http://127.0.0.1:8003", ] proxy_cycle = itertools.cycle(PROXIES) for i in range(
Salida real de esa ejecución:
solicitud 1: enviada a través de http://127.0.0.1:8001 -> proxy-1 solicitud 2: enviada a través de http://127.0.0.1:8002 -> proxy-2 solicitud 3: enviada a través de http://127.0.0.1:8003 -> proxy-3 solicitud 4: enviada a través de http://127.0.0.1:8001 -> proxy-1 solicitud 5: enviada a través de http://127.0.0.1:8002 -> proxy-2 solicitud 6: enviada a través de http://127.0.0.1:8003 -> proxy-3
itertools.cycle es el truco: es un iterador infinito que vuelve al inicio de la lista para siempre, así que next(proxy_cycle) siempre devuelve un proxy sin que tengas que rastrear un índice. Sustituye PROXIES por URLs de proxy reales y el bucle funciona exactamente de la misma manera: no tiene idea de si la lista provino de tres servidores de prueba locales o de una piscina comercial, que es el objetivo de probarlo de esta manera primero.
Patrones avanzados: conmutación por error y rotación concurrente
La rotación round-robin por sí sola no responde la pregunta que cada script de rotación eventualmente tiene que: ¿qué pasa cuando uno de los proxies está caído? El patrón a continuación selecciona un proxy aleatorio de la piscina, captura las excepciones específicas que requests lanza por fallos de proxy y conexión, y pasa al siguiente candidato en lugar de dejar que toda la solicitud falle:
import random import requests def get_with_rotation(url, proxy_pool, max_attempts=4, timeout=3): pool = list(proxy_pool) random.shuffle(pool) last_error =
ProxyError está documentado como una subclase de ConnectionError lanzada específicamente por fallos relacionados con proxies, que es por eso que se captura junto con el más general ConnectionError y Timeout. Ejecutar esto contra una piscina de cuatro direcciones —tres servidores locales reales y un puerto en el que no escucha nada— produjo esto en una ejecución:
intento 1: http://127.0.0.1:8004 falló (ProxyError), rotando sucedió a través de http://127.0.0.1:8003 -> proxy-3
La dirección caída falló de inmediato y el bucle continuó sin hacer que el script se bloqueara. Debido a que random.shuffle reordena la piscina en cada llamada, una ejecución diferente puede tener éxito en el primer intento si la mezcla sucede que coloca un proxy funcionando primero: ambos resultados son un comportamiento correcto para este patrón.
Rotar desde múltiples hilos agrega un requisito más: lo que elige "el siguiente proxy" debe ser seguro para llamar desde varios hilos a la vez. Un itertools.cycle compartido detrás de un threading.Lock, impulsado por ThreadPoolExecutor, mantiene el orden redondo intacto bajo concurrencia:
import itertools import threading from concurrent.futures import ThreadPoolExecutor, as_completed import requests PROXIES = ["http://127.0.0.1:8001", "http://127.0.0.1:8002", "http://127.0.0.1:8003"] _lock = threading.Lock() _cycle =
rutas = [f"/item/{i}" for i in range(9)] with ThreadPoolExecutor(max_workers=4) as pool: futuros = [pool.
Nueve solicitudes a través de cuatro hilos de trabajo todavía aterrizaron exactamente tres por proxy, en orden, porque el bloqueo serializa el acceso al iterador compartido a pesar de que las solicitudes en sí se ejecutan de manera concurrente. ThreadPoolExecutor es parte del módulo concurrent.futures de la biblioteca estándar: no se necesita ninguna dependencia adicional para este patrón.
Límites Honestamente: Lo Que Este Código de Rotación No Puede Arreglar
Este código decide qué URL de proxy se utiliza en cada solicitud; no tiene opinión sobre de dónde vienen esas URL o si son buenas. Una lista de tres IP muertas rotará exactamente de la misma manera que una lista de tres IP saludables: el bucle de recuperación anterior solo demuestra que puede recuperarse de algunos proxies malos, no que tu lista específica sea utilizable.
Retry de urllib3 no es un sustituto de la lógica de rotación en este artículo. Sus propios parámetros — total, backoff_factor, status_forcelist — describen reintentar la misma solicitud contra la misma conexión con un retraso de espera entre intentos; nada en urllib3.util.Retry cambia a un proxy diferente. Si montas un HTTPAdapter configurado con Retry en una sesión que tiene un proxy establecido, cada reintento todavía pasa por ese mismo proxy. Rotar a un proxy diferente en caso de fallo es lo que hace la función get_with_rotation anterior, y las dos técnicas deben combinarse, no elegirse entre sí.
Ning uno de los ejemplos aquí maneja límites de tasa de autenticación de proxy, semánticas de sesión pegajosa o enrutamiento por país: esas son propiedades de cualquier servicio de proxy que esté detrás de las URL, no algo que itertools.cycle o un bucle de reintentos pueden agregar.
Resolución de Problemas Comunes de Errores de Rotación
requests.exceptions.ProxyError significa que la conexión con el proxy en sí falló: el proxy está caído, el puerto es incorrecto o un firewall lo está bloqueando. Esto es lo que desencadena el puerto muerto en el ejemplo de recuperación anterior.
requests.exceptions.ConnectionError sin el subtipo más específico ProxyError generalmente significa que el proxy aceptó la conexión pero no pudo alcanzar el sitio de destino, lo que a menudo se presenta como un proxy que está cerca del final de su vida útil si estás extrayendo de un grupo rotatorio.
La rotación parece que no está ocurriendo a pesar de que tu bucle parece correcto: verifica primero si hay una variable de entorno HTTP_PROXY o HTTPS_PROXY, según la reproducción en vivo anterior en este artículo; sobrescribe silenciosamente session.proxies y hace que cada solicitud pase por la misma dirección independientemente de lo que tu bucle seleccionó.
Cada solicitud se agota en el mismo valor de timeout en lugar de fallar rápidamente en proxies muertos: disminuye el argumento timeout específicamente para comprobar la salud de un nuevo grupo, ya que un timeout generoso destinado a solicitudes reales hará que un proxy muerto se quede colgado durante toda la duración de cada intento de rotación.
Dirigiendo el Código de Rotación a un Grupo de Proxy Real
Los bucles en este artículo rotan a través de cualquier lista que les des, y cambiar a una puerta de enlace comercial significa cambiar la lista, no la lógica. Nstproxy es un proveedor de infraestructura de proxy que ofrece grupos de IP residenciales, de centro de datos, ISP estáticos, IPv6 y móviles, accesibles a través de HTTP, HTTPS o SOCKS5 a través de un único host de puerta de enlace en lugar de una lista de IP individuales que gestionas tú mismo. Su página de producto Residential Lite Proxies documenta el patrón de conexión directamente: un host y puerto de puerta de enlace fijos, con la rotación real de IP ocurriendo detrás de esa única dirección del lado del proveedor en lugar de en tu lista de Python: consulta la documentación de Nstproxy para conocer el host, puerto y parámetros de autenticación actuales antes de conectarlos a los ejemplos anteriores. Eso se ajusta a equipos que desean el comportamiento de rotación de este artículo sin mantener y comprobar la salud de su propia lista de IP de proxy individuales. La compensación es que estás confiando en el comportamiento de rotación del proveedor detrás de la puerta de enlace en lugar de controlarlo directamente como lo hacen los ejemplos de itertools.cycle y recuperación anteriores.
- Un host de puerta de enlace en lugar de una lista que mantener: el código del ejemplo básico de este artículo se reduce a un solo diccionario de proxy apuntado a esa única dirección de puerta de enlace en lugar de una lista de muchas; consulta el enlace de documentación anterior para conocer el host y puerto exactos antes de usarlo.
- Más de 50 millones de IP residenciales en más de 200 países y regiones: según la página del producto Residential Lite, lo que significa que la rotación que ocurre detrás de la puerta de enlace proviene de un grupo mucho más grande que la mayoría de las listas autogestionadas.
- Soporte para HTTP, HTTPS y SOCKS5: los mismos protocolos que los ejemplos de
requestsen este artículo ya utilizan, por lo que no hay cambios en el código de solicitud en sí. - Facturación de paquetes prepagados: los precios publicados de Nstproxy se venden en paquetes medidos en lugar de en suscripción, lo que importa si tu script de rotación se ejecuta solo ocasionalmente en lugar de continuamente.
Si estás rotando proxies específicamente para utilizar un navegador sin cabeza en lugar de simples llamadas de requests, configurar un proxy en Playwright abarca la versión del mismo problema en el contexto del navegador.
Conclusión
Rotar proxies en Python es un bucle que cambia un diccionario entre solicitudes, no es una función que instales: todo en este artículo, desde la versión de tres líneas de itertools.cycle hasta el grupo de failover seguro para hilos, se construye sobre ese mismo diccionario {"http": ..., "https": ...} que requests siempre ha aceptado. Comienza con el ciclo básico para confirmar que tu código de solicitud funciona, añade el bucle de failover una vez que apuntes a proxies que realmente pueden fallar, y solo usa hilos una vez que la latencia de una sola solicitud sea tu verdadero cuello de botella.
Preguntas Frecuentes
P: ¿Necesito una biblioteca especial para rotar proxies en Python, o requests lo maneja?
No necesitas una biblioteca especial: requests solo acepta un diccionario de proxy por llamada, y la rotación es el bucle en tu propio código que cambia qué diccionario se pasa en cada solicitud, como se muestra a lo largo de este artículo.
P: ¿Por qué parece que mi código de rotación siempre usa el mismo proxy?
Primero verifica si existe una variable de entorno HTTP_PROXY o HTTPS_PROXY, ya que la documentación de requests confirma que anula silenciosamente cualquier cosa que configures en session.proxies, lo cual este artículo reproduce en vivo.
P: ¿La clase Retry de urllib3 rota proxies por mí?
No: Retry reintenta la misma solicitud contra la misma conexión con un retraso de retroceso, y no cambia a un proxy diferente; necesitas un bucle de rotación como el ejemplo de failover en este artículo, junto con él, no en lugar de él.
P: ¿Es seguro rotar proxies desde múltiples hilos a la vez?
Es seguro siempre que lo que selecciona "el próximo proxy" esté protegido por un candado, como en el ejemplo de itertools.cycle protegido por threading.Lock mencionado arriba; un iterador compartido no protegido accedido desde múltiples hilos es la fuente habitual de sutiles errores de rotación bajo concurrencia.
P: ¿Cuál es la diferencia entre rotar mi propia lista de proxies y usar una puerta de enlace rotativa de un proveedor?
Rotar tu propia lista significa que tu código rastrea qué proxies están vivos y elige entre ellos, mientras que una puerta de enlace rotativa realiza esa misma tarea detrás de un host y puerto fijos del lado del proveedor, que es el patrón documentado por Nstproxy para sus Proxies Residenciales Lite.



