Volver al blog
Guías
Ștefan RăcilăLast updated on May 7, 202611 min read

Cómo utilizar proxies con Python Requests: De lo básico a la producción

Cómo utilizar proxies con Python Requests: De lo básico a la producción
En resumen: Esta guía explica paso a paso cómo utilizar proxies con Python Requests de principio a fin: un proxies , URL autenticadas, variables de entorno, Session reutilización, SOCKS5 sin fugas de DNS y un conjunto de rotación con reintentos y un cortacircuitos. Al final, sabrás cuándo una API gestionada sale a cuenta frente a un conjunto de servidores propio.

Introducción

Si alguna vez has lanzado un rastreador que funcionaba localmente y luego empezó a devolver errores 403, 429 o tiempos de espera silenciosos en producción, ya sabes por qué existen los proxies. Aprender a usar proxies con Python Requests marca la diferencia entre un script que se ejecuta una vez en tu portátil y un trabajo que sobrevive a los límites de velocidad, los bloqueos geográficos y las prohibiciones de IP en miles de páginas.

Una configuración de proxy de Python Requests, en su forma más simple, es un diccionario que mapea http y https a una URL de proxy y se pasa a requests.get(). Eso te permite desbloquearte durante diez minutos. La producción necesita más: credenciales fuera de Git, sesiones que mantengan las cookies, puntos finales SOCKS5 que no filtren el DNS, reintentos con retroceso y una estrategia de rotación que no siga insistiendo en un proxy inactivo.

Esta guía está dirigida a desarrolladores de Python de nivel intermedio que ya conocen los fundamentos de requests y ahora necesitan una forma fiable de añadir compatibilidad con proxies sin reescribir su scraper. Cubrimos cómo usar proxies con Python Requests, desde el simple diccionario hasta un bucle de rotación en producción, con las ventajas y desventajas explicadas en un lenguaje sencillo.

Inicio rápido: un proxy de Python Requests operativo en cinco minutos

Antes de profundizar en la rotación y los reintentos, aquí tienes el ejemplo de ocho líneas que el 90 % de los desarrolladores realmente necesitan cuando buscan cómo usar proxies con Python Requests. Cópialo en un archivo, sustituye el host:puerto por cualquier proxy que funcione y ejecútalo.

import requests

proxies = {
    "http":  "http://203.0.113.10:8080",
    "https": "http://203.0.113.10:8080",
}

resp = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=10)
print(resp.json())

Si la IP que se muestra es la dirección del proxy y no la tuya, tu proxy está en la ruta de la solicitud. El resto de esta guía trata sobre cómo reforzar este patrón.

Requisitos previos: Python, pip y un proxy al que puedas conectarte

Necesitas Python 3.8 o posterior (python --version), pip) y al menos un host:puerto de proxy que funcione. Un entorno virtual (python -m venv venv) mantiene las dependencias organizadas por proyecto. Instala Requests con pip install requests. El proxy puede proceder de una lista gratuita, un grupo de pago o una instancia local de Squid o Tor.

Cómo usar proxies con Python Requests: el modelo mental

Antes de meterte de lleno en el código, es útil saber cómo decide realmente Requests dónde enviar el tráfico. La biblioteca enruta cada llamada a través de una URL de proxy según el esquema: HTTP, HTTPS y (con un paquete adicional) SOCKS. Hay tres fuentes que pueden proporcionar esa URL, en este orden aproximado de prioridad: el proxies= argumento de una llamada individual, el session.proxies dict en un Sessiony, por último, las HTTP_PROXY / HTTPS_PROXY variables de entorno. La precedencia exacta y el manejo de variantes en minúsculas se documentan en la documentación de uso avanzado de Requests; compruébalo siempre con tu versión fijada.

Configurar un proxy básico con Python Requests

La configuración básica consta de dos pasos: crear un proxies diccionario y, a continuación, enviar una solicitud de verificación a través de él. Las dos subsecciones siguientes describen cada paso y los problemas que pueden surgir con proxies inactivos o mal configurados.

Crear el diccionario de proxies para HTTP y HTTPS

En Python Requests, los proxies se pasan como un diccionario que asigna esquemas a una URL de proxy. Rellena siempre ambas claves, incluso si solo tienes previsto acceder a destinos HTTPS, ya que las redirecciones pueden degradar el esquema.

proxies = {
    "http":  "http://user:pass@proxy.example.com:8080",
    "https": "http://user:pass@proxy.example.com:8080",
}
requests.get(url, proxies=proxies, timeout=(5, 15))

La timeout=(connect, read) tupla es imprescindible en producción. Sin ella, un proxy inactivo bloqueará tu worker.

Confirma que el proxy está en la ruta de la solicitud

Accede a un punto final de eco de IP y compáralo con tu IP real. Dos opciones fiables son https://api.ipify.org?format=json y https://httpbin.org/ip.

print(requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=10).json())

Si la dirección devuelta difiere de tu IP local, el proxy está funcionando. Si coincide, el proxy ha fallado en modo abierto de forma silenciosa.

Autentica los proxies y protege las credenciales

La mayoría de los proxies de pago están autenticados, y ahí es donde el uso de proxies con Python Requests se complica. Las tres subsecciones siguientes tratan sobre la incrustación de URL, las variables de entorno y los tres códigos de error que verás.

Incrustar un nombre de usuario y una contraseña en la URL del proxy

El formato aceptado es http://user:pass@host:port. Si tu contraseña contiene @, :, %, o /, codifícalo en URL o Requests analizará mal la URL y verás errores 407:

from urllib.parse import quote
user = quote("alice@corp")
pwd  = quote("p@ss:w/rd%1")
proxy_url = f"http://{user}:{pwd}@proxy.example.com:8080"

Nunca envíes esa cadena a Git.

Mueve los secretos a HTTP_PROXY, HTTPS_PROXY y NO_PROXY

Requests detecta automáticamente HTTP_PROXY, HTTPS_PROXY, y NO_PROXY del entorno y, según la documentación oficial, también respeta las variantes en minúsculas en sistemas POSIX. Eso significa que puedes mantener las credenciales completamente fuera del código:

# Linux / macOS
export HTTPS_PROXY="http://user:pass@proxy.example.com:8080"
export NO_PROXY="localhost,127.0.0.1,.internal"
# Windows
setx HTTPS_PROXY "http://user:pass@proxy.example.com:8080"

Este es el patrón más limpio para imágenes de Docker y ejecutores de CI, donde los secretos residen en el entorno y no en el repositorio.

Diagnosticar errores de proxy 407, 401 y 403

Cuando algo falla, el código de estado te indica qué capa presenta el problema.

Estado

Posible causa

Solución en una línea

407 Se requiere autenticación de proxy

Credenciales de proxy faltantes o malformadas

Codifica la contraseña en URL y vuelve a probar

401 No autorizado

Nombre de usuario o contraseña incorrectos

Cambie las credenciales y verifique con curl -x

403 Prohibido

El sitio de destino ha bloqueado la IP del proxy

Cambia a otro proxy o cambia la ubicación geográfica

Comprueba primero el proxy y luego el sitio de destino.

Reutiliza la configuración con requests.Session para las cookies y el agrupamiento de conexiones

A Session es la primitiva adecuada una vez que se realiza más de una llamada. Mantiene proxies, los encabezados predeterminados y las cookies, y mantiene activa la conexión TCP subyacente para que no tengas que realizar un nuevo protocolo de enlace TLS en cada solicitud. Session está integrado en Requests, por lo que no hay que instalar nada adicional.

session = requests.Session()
session.proxies = proxies
session.headers.update({"User-Agent": "my-scraper/1.0"})

session.post("https://example.com/login", data={"u": "alice", "p": "secret"})
dashboard = session.get("https://example.com/dashboard")  # cookies persist
print(dashboard.status_code, len(dashboard.content))

La misma sesión abarca .text, .json(), y .content, por lo que las descargas de texto, JSON y binarias pasan todas por el mismo proxy de sesión de Python Requests sin necesidad de reconfiguración.

Utiliza proxies SOCKS5 a través de requests[socks]

Requests no admite SOCKS de forma nativa. Incorpora PySocks con el socks extra:

pip install "requests[socks]"

A continuación, utiliza el socks5h:// esquema. El h le indica a PySocks que resuelva el DNS a través del proxy en lugar de hacerlo localmente, que es lo que quieres cuando no confías en el resolutor de tu ISP o estás utilizando Tor.

proxies = {
    "http":  "socks5h://127.0.0.1:9050",  # Tor default
    "https": "socks5h://127.0.0.1:9050",
}
requests.get("https://check.torproject.org/", proxies=proxies, timeout=15)

Plain socks5:// resuelve el DNS localmente y filtra silenciosamente los nombres de host que visitas.

Rota los proxies para evitar bloqueos y límites de velocidad

Una sola IP puede sufrir limitaciones de velocidad y, finalmente, ser bloqueada. La verdadera respuesta a cómo utilizar proxies con Python Requests a gran escala es la rotación, y las tres subsecciones siguientes muestran patrones de madurez creciente.

Rotación aleatoria con un bucle de reintentos

El patrón más sencillo es random.choice recorrer una lista de proxies, envuelta en un bucle de reintentos:

import random, requests
from requests.exceptions import RequestException

PROXIES = [{"http": p, "https": p} for p in PROXY_URLS]

def fetch(url, attempts=4):
    for _ in range(attempts):
        proxy = random.choice(PROXIES)
        try:
            return requests.get(url, proxies=proxy, timeout=10)
        except RequestException:
            continue
    raise RuntimeError("all attempts failed")

Funciona, pero la aleatoriedad pura selecciona repetidamente proxies inactivos e ignora la carga.

Opciones de potencia de dos para un equilibrio de carga más inteligente

Un refinamiento bien estudiado son las elecciones de potencia de dos: para cada solicitud, se seleccionan dos proxies al azar y se utiliza el que esté gestionando menos llamadas en curso. La intuición, respaldada por la literatura sobre equilibrio de carga y comúnmente atribuida al análisis de Mitzenmacher de 2001, es que esto amortigua la carga en el peor de los casos mucho mejor que la aleatoriedad uniforme, sin dejar de ser económico.

import random
LOAD = {p: 0 for p in PROXY_URLS}

def pick():
    a, b = random.sample(PROXY_URLS, 2)
    return a if LOAD[a] <= LOAD[b] else b

Incrementar LOAD[proxy] antes de la solicitud y decrementar después. Las ganancias exactas dependen del tamaño del grupo; realice pruebas de rendimiento antes de citar cifras.

Añada un cortacircuitos para que los proxies inactivos dejen de desperdiciar solicitudes

Tanto el método aleatorio como el de potencia de dos siguen seleccionando un proxy inactivo hasta que tiene éxito. Un disyuntor soluciona eso. Realiza un seguimiento del estado por proxy: CLOSED (en buen estado), OPEN (omitido) y HALF_OPEN (en periodo de prueba).

import time
state = {p: {"fail": 0, "open_until": 0} for p in PROXY_URLS}
MAX_FAILS, COOLDOWN = 3, 60

def usable(p):
    return time.time() >= state[p]["open_until"]

def record(p, ok):
    if ok:
        state[p]["fail"] = 0
    else:
        state[p]["fail"] += 1
        if state[p]["fail"] >= MAX_FAILS:
            state[p]["open_until"] = time.time() + COOLDOWN

Tras el tiempo de espera, envía al proxy una solicitud en periodo de prueba antes de restablecerlo por completo.

Reintentar las solicitudes fallidas con HTTPAdapter y urllib3 Reintentar

Montar un HTTPAdapter con una urllib3 Retry política en una sesión aplica reintentos a todas las llamadas HTTP y HTTPS de esa sesión. Fijar urllib3 (por ejemplo, urllib3==2.2.*) para que los nombres de los parámetros se mantengan estables tras las actualizaciones.

from requests import Session
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

retry = Retry(
    total=3,
    status_forcelist=[429, 500, 502, 503, 504],
    backoff_factor=2,
    allowed_methods=["GET", "POST"],
    respect_retry_after_header=True,
)
adapter = HTTPAdapter(max_retries=retry)
s = Session()
s.mount("http://", adapter)
s.mount("https://", adapter)

Con backoff_factor=2, urllib3 espera aproximadamente backoff_factor * (2 ** (n - 1)) segundos entre intentos (aproximadamente 2, 4, 8 s). Combina los reintentos con la rotación para que cada reintento elija también un nuevo proxy.

Gestiona la verificación SSL y los certificados de proxy autofirmados

Si un proxy presenta un certificado autofirmado, verify=False silencia la advertencia pero te expone a ataques de intermediario, así que utilízalo solo en proxies locales de confianza o en pruebas. La solución más segura es añadir el proxy o el paquete de CA corporativo al almacén de confianza mediante verify="/path/to/ca.pem" o REQUESTS_CA_BUNDLE. Suprima InsecureRequestWarning solo después de haber sopesado deliberadamente los riesgos de seguridad.

Cuándo abandonar el grupo de proxies DIY por una API de scraping gestionada

Revisa esta lista de verificación. Si marcas tres o más puntos, un proxy gestionado o una API de scraping suele ser más barato que tu tiempo:

  • Necesitas segmentación geográfica en más de dos países.
  • Las prohibiciones suponen una pérdida de ingresos reales, no solo un nuevo intento.
  • Los objetivos renderizan contenido con JavaScript.
  • Un ingeniero sénior dedica un día a la semana a supervisar el grupo de proxies.
  • El cumplimiento normativo exige direcciones IP residenciales auditadas.

Puntos clave

  • La respuesta más breve a cómo usar proxies con Python Requests es un diccionario que mapea http y https a una URL de proxy, pasada a través de proxies= con un timeout.
  • Mantén las credenciales fuera del código fuente: es preferible HTTP_PROXY, HTTPS_PROXY, y NO_PROXY variables de entorno, y codifica los caracteres especiales de las contraseñas en URL.
  • Un requests.Session persiste proxies, encabezados y cookies, y reutiliza las conexiones TCP, lo cual es el valor predeterminado adecuado para cualquier flujo de trabajo con múltiples llamadas.
  • La rotación en producción combina opciones de potencia de dos con un disyuntor y una HTTPAdapter Retry política que sobrevive a los 429 y 5xx.
  • Para SOCKS5, instala requests[socks] y utilice socks5h:// para que el DNS se resuelva a través del proxy en lugar de filtrarse localmente.

Recursos relacionados de WebScrapingAPI

Preguntas frecuentes

¿Python Requests es compatible con proxies SOCKS5 de forma nativa?

No. La requests solo incluye compatibilidad con HTTP y HTTPS. Ejecuta pip install "requests[socks]" para instalar PySocks y, a continuación, utiliza un socks5:// o, preferiblemente, socks5h:// una URL en tu proxies dict. Esa es la forma más limpia de conseguir compatibilidad con SOCKS.

¿Por qué mis solicitudes proxy siguen revelando mi IP real a través de las consultas DNS?

Porque el socks5:// esquema le indica a PySocks que resuelva los nombres de host localmente antes de tunelizar la conexión. Cambia a socks5h://, donde el h significa resolución remota de nombres de host, por lo que las consultas DNS pasan por el servidor SOCKS. Esto es especialmente importante para Tor o cualquier modelo de amenaza en el que tu resolutor DNS no sea de confianza o se registre.

¿Cómo codifico en URL una contraseña de proxy que contiene los caracteres @, : o %?

Utiliza urllib.parse.quote de la biblioteca estándar: quote("p@ss:w/rd%1") se convierte en p%40ss%3Aw%2Frd%251. Incorpora el valor codificado en http://user:encoded_pwd@host:port. Sin codificar, esos caracteres terminan el segmento de información de usuario antes de tiempo, y verás un error 407 Proxy Authentication Required incluso cuando la contraseña sea técnicamente correcta.

¿Cómo le indico a Python Requests que omita el proxy para localhost o dominios internos?

Establece NO_PROXY una lista separada por comas de hosts o sufijos de dominio, por ejemplo NO_PROXY="localhost,127.0.0.1,.internal,.svc.cluster.local". Requests distingue entre mayúsculas y minúsculas en sistemas POSIX. Para anular la configuración por llamada, pasa proxies={"http": None, "https": None} para omitir cualquier proxy a nivel de sesión.

¿Cuándo debo pasar de un grupo de proxies rotativos DIY a una API de scraping gestionada?

Cuando el coste operativo supera la factura. Factores concretos: las prohibiciones cuestan más que un reintento, necesitas IP residenciales en varios países, los objetivos tienen mucho JavaScript o dedicas más de unas pocas horas de ingeniería a la semana a ajustar el grupo. Por debajo de eso, un pequeño grupo propio con reintentos y un disyuntor suele ser suficiente.

Conclusión

Saber cómo usar proxies con Python Requests no se trata tanto de un truco concreto como de una cuestión de capas: un proxies dict para empezar, credenciales en variables de entorno para que los secretos no entren en git, un Session para la reutilización de conexiones y cookies, socks5h:// cuando las fugas de DNS importan, y rotación más reintentos cuando una IP ya no es suficiente. Combina opciones de potencia de dos con un disyuntor y una HTTPAdapter Retry política, y tu rastreador dejará de colapsarse en el momento en que un proxy deje de funcionar o un objetivo devuelva errores 429.

En algún momento, todos los equipos llegan a un punto en el que mantener el grupo de proxies cuesta más de lo que valen los datos. Si tus objetivos están muy protegidos, son específicos de una zona geográfica o se renderizan con JavaScript, una opción gestionada como la API WebScrapingAPI Scraper se encarga de la capa de solicitudes, la rotación y el desbloqueo detrás de un único punto de acceso, por lo que conservas el código de análisis que ya has escrito y solo cambias el paso de obtención. Utiliza la lista de verificación anterior para decidir; si hay tres o más casillas marcadas, las cuentas favorecen la infraestructura gestionada frente a otra ronda de mantenimiento del pool. En cualquier caso, los patrones de esta guía deberían mantener tu requestscódigo basado en

Acerca del autor
Ștefan Răcilă, Desarrollador Full Stack @ WebScrapingAPI
Ștefan RăcilăDesarrollador Full Stack

Stefan Racila es ingeniero de DevOps y Full Stack en WebScrapingAPI, donde se encarga de desarrollar funciones para los productos y de mantener la infraestructura que garantiza la fiabilidad de la plataforma.

Empieza a crear

¿Estás listo para ampliar tu recopilación de datos?

Únete a más de 2000 empresas que utilizan WebScrapingAPI para extraer datos de la web a escala empresarial sin ningún gasto de infraestructura.