Proxies

Proxy en Python Requests: Configuración, Autenticación y Rotación

24 ago 202613 min de lectura

Configurar un python requests proxy es el primer paso real en casi cualquier trabajo de scraping o monitoreo, y es una de esas tareas que parecen triviales hasta que una solicitud devuelve en silencio tu propia IP o un error de autenticación que no puedes explicar. Esta guía lo soluciona. Al final tendrás código listo para copiar y pegar para enrutar la biblioteca requests a través de un proxy por HTTP, HTTPS y SOCKS5, añadir autenticación, reutilizar una conexión con una sesión, definir timeouts y retries, rotar por un pool, verificar si la IP realmente cambió y manejar los errores que encontrarás en la práctica. El caso de uso honesto a lo largo del texto es trabajo legítimo - scraping de datos públicos, monitoreo de precios y de SERP y análisis SEO - no algo detrás de un inicio de sesión.

Última actualización: 24 de agosto de 2026. Esta publicación contiene enlaces de afiliado - consulta nuestra divulgación de afiliados. Los planes y endpoints de los proveedores cambian, así que verifica cualquier cifra en el propio sitio del proveedor antes de confiar en ella.

Cómo definir un proxy en Python requests

El ejemplo funcional mínimo es un diccionario con las claves http y https, pasado al argumento proxies de cualquier método de solicitud:

import requests

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

response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(response.json())

Dos cosas hacen que la gente tropiece aquí. Primero, el valor es una URL de proxy completa, no una IP pelada: necesita el esquema (http://), el host y el puerto. Segundo, las claves del diccionario no son el protocolo del proxy - seleccionan qué proxy usar según el esquema de la URL de destino. Una solicitud a un sitio https:// usa la clave https. Esa distinción se vuelve importante en la siguiente sección, y es la razón aislada más común de que un proxy "no funcione".

Pasa siempre un timeout, como arriba. Un proxy se sitúa entre tú y el objetivo, así que un proxy muerto o lento colgará tu script para siempre sin uno.

Proxies HTTP, HTTPS y SOCKS5

Las claves del diccionario proxies enrutan por el esquema de destino, mientras que el esquema del valor es lo que habla el propio proxy. Así, un único proxy HTTP capaz de tunelizar tráfico HTTPS se escribe con un valor http:// bajo ambas claves - que es exactamente lo que son la mayoría de los proxies HTTP de datacenter y residenciales. No necesitas un valor https:// solo porque estés haciendo scraping de sitios HTTPS.

SOCKS5 es distinto. Necesita una dependencia adicional:

pip install "requests[socks]"

Luego usa el esquema socks5h://. La h final importa: envía la resolución de DNS a través del proxy en vez de resolver los hostnames localmente, que es lo que casi siempre quieres para scraping, para que el DNS de tu máquina no filtre el objetivo.

proxies = {
    "http": "socks5h://user:pass@proxy-host:1080",
    "https": "socks5h://user:pass@proxy-host:1080",
}

response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(response.json())
Prefijo del esquemaQué hace
http://Enruta por un proxy HTTP; tuneliza objetivos HTTPS mediante CONNECT. El valor por defecto para la mayoría de los proxies.
socks5://Enruta por SOCKS5, pero resuelve el DNS localmente en tu máquina.
socks5h://Enruta por SOCKS5 y resuelve el DNS en el proxy (la h). Preferido para scraping.

Autenticación de proxy en Python

La mayoría de los proxies de pago exigen un nombre de usuario y una contraseña. Con requests, las credenciales van directas a la URL del proxy como http://user:pass@host:port:

proxy_url = "http://my-username:my-password@proxy-host:8080"
proxies = {"http": proxy_url, "https": proxy_url}

response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(response.json())

Aquí está el bug que le cuesta una tarde a la gente: si tu contraseña contiene caracteres como @, :, # o /, rompen el análisis de la URL y la autenticación falla sin motivo obvio. Haz URL-encode del nombre de usuario y la contraseña con urllib.parse.quote antes de construir la URL:

from urllib.parse import quote

user = "my-username"
password = "p@ss:w#rd"       # contains reserved characters
host, port = "proxy-host", 8080

proxy_url = f"http://{quote(user, safe='')}:{quote(password, safe='')}@{host}:{port}"
proxies = {"http": proxy_url, "https": proxy_url}

El argumento safe='' le indica a quote que codifique todo carácter reservado en lugar de dejar algunos intactos. Hazlo desde el principio y nunca perseguirás un bug fantasma de "la autenticación falla sin razón". Si el proxy aún te rechaza, el código de estado a buscar es 407 Proxy Authentication Required - tratado en la sección de errores más abajo.

Reutiliza conexiones con requests.Session

Para cualquier cosa más allá de una solicitud puntual, usa una Session. Define los proxies una vez en la sesión y cada solicitud a través de ella reutiliza el proxy, el cookie jar y la conexión TCP subyacente:

session = requests.Session()
session.proxies = {
    "http": proxy_url,
    "https": proxy_url,
}

session.get("https://httpbin.org/ip", timeout=10)
session.get("https://httpbin.org/headers", timeout=10)  # same proxy, same cookies

Esto importa para el scraping de dos maneras. Las cookies persisten, así que un inicio de sesión o un estado de sesión se traslada entre solicitudes, y la reutilización de conexión recorta la sobrecarga del handshake en cada llamada, lo que es una ganancia real de velocidad cuando haces miles de solicitudes por el mismo proxy.

Define proxies con variables de entorno

requests lee la configuración de proxy del entorno automáticamente, así que puedes mantener las credenciales totalmente fuera de tu código:

export HTTP_PROXY="http://user:pass@proxy-host:8080"
export HTTPS_PROXY="http://user:pass@proxy-host:8080"

Se reconocen tanto los nombres en mayúsculas como en minúsculas (http_proxy, https_proxy). Unas notas sobre cuándo esto queda más limpio:

  • Bueno para cambiar proxies por entorno (dev, staging, production) sin tocar el código.
  • Bueno para mantener los secretos en un archivo .env o en el almacén de secretos de tu CI, en vez de en el repositorio.
  • Para ignorar los proxies de entorno en una sesión, define session.trust_env = False. Esto es útil cuando hay un proxy a nivel de máquina pero quieres que un script específico salga directo o use su propia lista.

Añade timeouts y retries

Esta es la parte que la mayoría de los tutoriales se salta, y es la diferencia entre un script que muere a las 3 de la madrugada y uno que termina. Los proxies son más inestables que una conexión directa - una IP puede estar temporalmente sobrecargada o con rate limit - así que los retries importan aquí más de lo habitual.

Empieza con un timeout de verdad. Pasar una tupla lo divide en timeouts de conexión y de lectura, así que un proxy lento falla rápido en la conexión pero aún permite que una respuesta lenta termine:

response = requests.get(url, proxies=proxies, timeout=(5, 20))  # (connect, read) seconds

Luego monta una estrategia de retry en una sesión usando el Retry de urllib3 con un HTTPAdapter. El backoff_factor espacia los intentos para que no machaques un endpoint en apuros, y status_forcelist reintenta en los códigos transitorios de servidor y de rate limit:

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

retry = Retry(
    total=5,
    backoff_factor=1,                       # waits 0s, 2s, 4s, 8s... between tries
    status_forcelist=[429, 500, 502, 503, 504],
    allowed_methods=["GET", "POST"],
)

session = requests.Session()
adapter = HTTPAdapter(max_retries=retry)
session.mount("http://", adapter)
session.mount("https://", adapter)
session.proxies = {"http": proxy_url, "https": proxy_url}

response = session.get("https://httpbin.org/ip", timeout=(5, 20))
print(response.json())

En versiones más antiguas de urllib3 el argumento se llama method_whitelist en vez de allowed_methods - actualiza la biblioteca si te topas con eso. Ten en cuenta que Retry maneja los errores de conexión y los códigos de estado que enumeras; no reintenta un 200 limpio que simplemente devolvió una página de bloqueo, y por eso las secciones de rotación y de errores de más abajo siguen importando.

Rota proxies por un pool

Enviar cada solicitud desde una sola IP es la forma más rápida de recibir rate limit o bloqueo. La rotación reparte las solicitudes entre muchas IP. Hay dos enfoques honestos, y valen el esfuerzo a escalas muy distintas. Si quieres el concepto primero, mira nuestra explicación sobre cómo funcionan los proxies rotativos.

Método A - un pool manual con random.choice

Si tienes un puñado de IP estáticas, mantenlas en una lista y elige una por solicitud. random.choice da una selección aleatoria simple:

import random
import requests

proxy_pool = [
    "http://user:pass@ip-1:8080",
    "http://user:pass@ip-2:8080",
    "http://user:pass@ip-3:8080",
]

targets = ["https://httpbin.org/ip"] * 6
for url in targets:
    proxy = random.choice(proxy_pool)
    proxies = {"http": proxy, "https": proxy}
    try:
        r = requests.get(url, proxies=proxies, timeout=10)
        print(proxy, "->", r.json()["origin"])
    except requests.exceptions.RequestException as exc:
        print(f"failed via {proxy}: {exc}")

Para un round-robin estricto en vez de aleatorio, usa itertools.cycle y llama a next(proxy_cycle) cada vez. El Método A sirve para un conjunto pequeño y fijo de IP que ya posees. El costo es que mantienes la lista, depuras las IP muertas y llevas la cuenta de cuáles ha empezado a bloquear un objetivo.

Método B - un endpoint rotativo del proveedor

A escala, dejas de mantener una lista y apuntas cada solicitud a un único endpoint de backbone, dejando que el proveedor cambie la IP de salida por ti. Usando el formato real de endpoint rotativo de Webshare, añades -rotate al nombre de usuario y envías todo a p.webshare.io en el puerto 80:

import requests

# One endpoint; the provider hands you a fresh exit IP per request.
proxy = "http://<username>-rotate:<password>@p.webshare.io:80/"
proxies = {"http": proxy, "https": proxy}

for _ in range(6):
    r = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
    print(r.json()["origin"])   # typically a different IP each call

El sufijo -rotate pide una IP nueva por llamada; si necesitas que la misma IP persista para un flujo de varios pasos (una sesión sticky), los proveedores te dan en su lugar una forma de id de sesión numérica. Verifica el endpoint exacto y el formato del nombre de usuario en help.webshare.io antes de confiar en ellos, ya que pueden cambiar. El punto del Método B es simple: elimina toda la carga de mantenimiento del Método A, y por eso gana en cuanto pasas de unas pocas IP.

Para de verdad ejecutar cualquiera de estos fragmentos sin comprometerte con un plan, el nivel gratuito de Webshare te da 10 proxies y 1GB de ancho de banda mensual (a agosto de 2026, verifica en el sitio), lo que basta para probar la rotación, la autenticación y el código de retry de principio a fin. Puedes conseguir los proxies gratuitos aquí y pegar tus credenciales reales en los ejemplos de arriba.

Una pequeña nota honesta de mi propio uso: aunque el fragmento de arriba rota por solicitud, para mi scraping en realidad fijo una sesión sticky en vez de tomar una IP nueva en cada llamada, y mantuvo la misma IP de salida todo el tiempo que corrió el trabajo. Configurar eso a través del endpoint de Webshare fue directo, y no me topé con errores de autenticación 407 ni de rate 429, así que nunca necesité un espaciado fuerte de solicitudes para sortearlos. Tus resultados variarán según el objetivo, así que mide la tasa de éxito contra tus propias URL antes de escalar un trabajo.

Verifica la IP de tu proxy

Antes de confiar en nada de esto, confirma que el proxy está de verdad en la ruta. Golpea un servicio de eco de IP con y sin el proxy y compara - si ambos coinciden, el proxy no se está usando:

import requests

def show_ip(proxies=None):
    r = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=10)
    return r.json()["ip"]

print("direct:   ", show_ip())
print("via proxy:", show_ip({"http": proxy_url, "https": proxy_url}))

Si la línea "via proxy" aún muestra tu IP real, la causa habitual es una clave https ausente en el diccionario mientras solicitas una URL https:// - así que la solicitud se fue directa sin ruido. Añade la entrada https (mira la sección de configuración) y vuelve a ejecutar. Puedes cambiar api.ipify.org por https://httpbin.org/ip; solo lee el campo origin en vez de ip.

Maneja los errores de proxy

Envuelve las solicitudes en un try/except y captura las excepciones específicas para reaccionar correctamente en vez de tumbar toda la ejecución:

import requests

try:
    r = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
    r.raise_for_status()
    print(r.json())
except requests.exceptions.ProxyError as exc:
    print("proxy dead or blocked:", exc)
except requests.exceptions.ConnectTimeout:
    print("proxy did not answer in time")
except requests.exceptions.HTTPError as exc:
    print("target returned an error status:", exc.response.status_code)
except requests.exceptions.RequestException as exc:
    print("request failed:", exc)

Captura de lo más específico a lo más general; RequestException es la clase base y debe quedar en último lugar como atrapatodo. Así se decodifican los fallos comunes:

SíntomaCausa probableSolución
"Max retries exceeded" / ProxyErrorEndpoint de proxy muerto, incorrecto o bloqueadoRevisa host/puerto, rota a otra IP, confirma que el proxy está vivo
HTTP 407Autenticación de proxy rechazadaCorrige el usuario/contraseña; haz URL-encode de la contraseña con quote
HTTP 429Rate limit por parte del objetivoBaja el ritmo, añade backoff, rota IP, espacia las solicitudes
HTTP 403El objetivo bloqueó esta IP o fingerprintRota IP, prueba un tipo de proxy más sigiloso, revisa los headers de la solicitud
ConnectTimeoutEl proxy está activo pero lento o sobrecargadoSube el timeout de conexión, reintenta, saca la IP lenta del pool

Haz scraping con responsabilidad

Los proxies son una herramienta para trabajo legítimo: scraping de datos públicos, monitoreo de precios y de SERP, verificación de anuncios, investigación de mercado y datos de SEO. Mantenlo limpio - respeta el robots.txt y los Términos de Servicio de cada sitio, apunta solo a datos disponibles públicamente, regula tu tasa de solicitudes para no degradar el servidor de otra persona y mantente lejos de cualquier cosa detrás de un inicio de sesión. Esto es una buena práctica general, no asesoría legal; revisa las reglas que apliquen a tu propio proyecto y jurisdicción. Para el panorama más amplio de cómo hacer esto bien, mira nuestra guía de proxies para web scraping.

Preguntas frecuentes

¿La biblioteca requests admite SOCKS5?

Sí, una vez que instalas el extra con pip install "requests[socks]". Luego usa un valor socks5h:// en el diccionario de proxies para que el DNS se resuelva a través del proxy.

¿Por qué mi solicitud sigue mostrando mi IP real?

Casi siempre es una clave https ausente mientras solicitas una URL HTTPS, así que la solicitud se fue directa. Asegúrate de que ambas claves http y https estén definidas y vuelve a ejecutar el fragmento de verificación de arriba.

¿Los proxies gratuitos sirven para scraping en Python?

Para aprender el código, claro. Para trabajos reales son lentos, de vida corta y a menudo ya bloqueados o inseguros. Un nivel gratuito genuino de un proveedor de pago (IP limitadas pero fiables) es una mejor manera de probar exactamente el código antes de pagar por volumen.

¿Cómo obtengo una IP nueva en cada solicitud?

O rotas un pool tú mismo con random.choice (Método A), o apuntas cada solicitud a un endpoint rotativo de proveedor como <username>[email protected]:80 (Método B), que cambia la IP de salida por ti.

¿Puedo usar la misma configuración de proxy con httpx o aiohttp?

El concepto se traslada, pero la sintaxis difiere - httpx y aiohttp reciben una URL de proxy o config por cliente, en vez del mismo diccionario. Para la línea de comandos, mira nuestra guía para usar un proxy con curl, y para el panorama más amplio nuestra visión general de proxies para web scraping.

Conclusión

El camino es siempre el mismo: construye el diccionario proxies, añade autenticación (haciendo URL-encode de la contraseña), reutiliza una Session, define timeouts y un adaptador Retry, y luego rota - un pool manual para unas pocas IP, un endpoint rotativo de proveedor en cuanto escalas. Verifica que la IP cambió, captura los errores y haz scraping solo de datos públicos dentro de las reglas de cada sitio. A la hora de elegir un proveedor, nuestra recopilación de los mejores servicios de proxy compara las opciones con honestidad. Para ejecutar todo lo anterior sin gastar nada primero, consigue los proxies gratuitos de Webshare, coloca tus credenciales en los fragmentos y confirma que cada bloque funciona contra tus propios objetivos antes de escalar.

J

Jasmine

Autora · Jasmine Daily

La autora detrás de Jasmine Daily, anotando pensamientos, experiencias y momentos cotidianos. Honesta, sin prisa, imperfecta.

Jasmine Daily

Hay más esperando a ser leído.

Si este texto te llegó, explora algunas páginas más del diario.

Leer a continuación

Entradas relacionadas