Guía práctica: Selenium proxy auth, proxies residenciales y stealth en 2025

Aprende a configurar proxies autenticados en Selenium con selenium-wire, Firefox profiles, stealth, rotación de IPs y despliegue en Grid. Comparativa con Playwright incluida.

Guía práctica: Selenium proxy auth, proxies residenciales y stealth en 2025

El problema con Selenium proxy auth

Si alguna vez intentaste pasar --proxy-server=http://user:pass@host:port a ChromeDriver, ya sabes el resultado: Chrome ignora las credenciales y lanza un popup de autenticación que bloquea la ejecución. Este es uno de los dolores más persistentes para QA engineers y scraping teams que trabajan con Selenium residential proxies.

El problema no es un bug — es una decisión de diseño. Chromium delega la autenticación proxy al sistema operativo, no al proceso del navegador. Selenium, al envolver Chrome vía DevTools Protocol, no tiene forma nativa de inyectar credenciales en esa capa. El resultado: necesitas workarounds.

En esta guía cubrimos cada enfoque disponible — desde selenium-wire para Chrome hasta perfiles Firefox, pasando por Selenium stealth, rotación de IPs y despliegue en Grid con contenedores. Al final, una comparativa honesta con Playwright.

Chrome + selenium-wire: proxies autenticados sin hacks

selenium-wire extiende el WebDriver de Chrome para interceptar y modificar requests a nivel de proxy. En lugar de depender de que Chrome maneje la autenticación, selenium-wire la inyecta directamente en la conexión TCP.

Instalación y configuración base

pip install selenium-wire selenium

from seleniumwire import webdriver
from selenium.webdriver.chrome.options import Options

proxy_user = "user-country-US-session-sess01"
proxy_pass = "PASSWORD"
proxy_host = "gate.proxyhat.com"
proxy_port = 8080

options = Options()
options.add_argument("--headless=new")
options.add_argument("--disable-blink-features=AutomationControlled")

seleniumwire_options = {
    "proxy": {
        "https": f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}",
        "http": f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}",
    },
    "disable_encoding": True,  # evita problemas con content-encoding
}

driver = webdriver.Chrome(
    options=options,
    seleniumwire_options=seleniumwire_options,
)

driver.get("https://httpbin.org/ip")
print(driver.page_source)
driver.quit()

Puntos clave de selenium-wire

  • Interceptación transparente: las credenciales se inyectan antes de que la request salga del proceso de Python.
  • Acceso a requests: puedes inspeccionar driver.requests y driver.wait_for_request() — útil para verificar que el proxy responde.
  • Overhead: selenium-wire añade un proxy local (mitmproxy bajo el hood). Espera ~50-100ms extra por request y un consumo de memoria ~2x respecto a Selenium vanilla.
  • Compatibilidad: funciona con Chrome y Edge. Firefox no está soportado — para Firefox usa el enfoque de perfil que veremos después.
Cuándo usarlo: cuando necesitas Selenium proxy auth en Chrome sin tocar el sistema operativo, y el overhead de memoria es aceptable para tu volumen de scraping.

Firefox: proxy autenticado vía perfil generado

Firefox sí permite configurar proxies autenticados directamente en el perfil — sin necesidad de librerías externas. El truco es crear un perfil programáticamente y escribir las preferencias antes de iniciar el navegador.

from selenium import webdriver
from selenium.webdriver.firefox.options import Options as FirefoxOptions
import tempfile, os, configparser

def create_firefox_proxy_profile(proxy_user, proxy_pass, proxy_host, proxy_port):
    """Crea un perfil Firefox con proxy autenticado."""
    profile_dir = tempfile.mkdtemp(prefix="ff_proxy_")

    # Escribir preferencias de proxy directamente en user.js
    prefs = {
        "network.proxy.type": 1,  # manual
        "network.proxy.http": proxy_host,
        "network.proxy.http_port": proxy_port,
        "network.proxy.ssl": proxy_host,
        "network.proxy.ssl_port": proxy_port,
        "network.proxy.socks": proxy_host,
        "network.proxy.socks_port": 1080,
        "network.proxy.socks_version": 5,
        "network.proxy.no_proxies_on": "localhost, 127.0.0.1",
        # Credenciales — solo funcionan con extensions o auto-auth
        "signon.autologin.network": True,
    }

    user_js = os.path.join(profile_dir, "user.js")
    with open(user_js, "w") as f:
        for key, val in prefs.items():
            if isinstance(val, bool):
                f.write(f'user_pref("{key}", {str(val).lower()});\n')
            elif isinstance(val, str):
                f.write(f'user_pref("{key}", "{val}");\n')
            else:
                f.write(f'user_pref("{key}", {val});\n')

    return profile_dir


proxy_user = "user-country-DE-city-berlin"
proxy_pass = "PASSWORD"

profile_dir = create_firefox_proxy_profile(
    proxy_user, proxy_pass,
    "gate.proxyhat.com", 8080
)

options = FirefoxOptions()
options.add_argument("-headless")
options.add_argument(f"-profile")
options.add_argument(profile_dir)

driver = webdriver.Firefox(options=options)

# Para credenciales: inyectar una extension que maneje el auth popup
# Alternativa: usar selenium-wire con Firefox (no soportado oficialmente)
# Mejor opción para Firefox + auth: extensiones de auto-auth

driver.get("https://httpbin.org/ip")
print(driver.page_source)
driver.quit()

Firefox no envía credenciales automáticamente desde user.js. Necesitas una extensión que capture el diálogo de autenticación y lo rellene. La extensión Close Proxy Auth (disponible en GitHub) hace exactamente esto — intercepta el 407 Proxy Auth Required y reenvía las credenciales.

Ventaja Firefox: el perfil es portable — puedes pregenerarlo una vez, cachearlo en disco y reutilizarlo en cada sesión. Ideal para flotas de contenedores donde cada instancia necesita un perfil limpio.

Selenium stealth: reduciendo fingerprints de automatización

Incluso con el proxy correcto, los sitios pueden detectarte por browser fingerprints: navigator.webdriver, window.chrome, permisos de plugins, y docenas de señales más. Aquí entra Selenium stealth.

selenium-stealth: parches rápidos

pip install selenium-stealth

from selenium import webdriver
from selenium_stealth import stealth

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--disable-gpu")

driver = webdriver.Chrome(options=options)

stealth(driver,
    languages=["es-ES", "es", "en-US", "en"],
    vendor="Google Inc.",
    platform="Win32",
    webgl_vendor="Intel Inc.",
    renderer="Intel Iris OpenGL Engine",
    fix_hairline=True,
)

# Verificar que navigator.webdriver es undefined
driver.get("https://bot.sannysoft.com/")
print("WebDriver flag:", driver.execute_script("return navigator.webdriver"))
driver.quit()

selenium-stealth parchea ~15 propiedades del navegador en runtime. Es rápido de integrar pero tiene limitaciones:

  • No evita detección de Canvas fingerprinting avanzado.
  • No modifica WebGL hash — sitios sofisticados como Cloudflare Turnstile aún pueden detectarlo.
  • Es un parche, no una reescritura del navegador.

selenium-driverless: la opción más agresiva

selenium-driverless toma un enfoque radical: en lugar de usar ChromeDriver, se comunica directamente con Chrome vía CDP (Chrome DevTools Protocol), eliminando la capa que Cloudflare y otros detectan.

pip install selenium-driverless

from selenium_driverless import webdriver as driverless_webdriver
from selenium_driverless.types import By

async def scrape_with_driverless():
    options = driverless_webdriver.ChromeOptions()
    options.add_argument("--headless=new")

    # Proxy via flag de Chrome (sin auth)
    options.add_argument("--proxy-server=http://gate.proxyhat.com:8080")

    driver = await driverless_webdriver.Chrome(options=options)

    # Navegar
    await driver.get("https://nowsecure.nl/")
    title = await driver.title
    print(f"Page title: {title}")

    await driver.quit()

import asyncio
asyncio.run(scrape_with_driverless())

Nota: selenium-driverless no soporta autenticación proxy nativamente. Combínalo con IP autenticada (whitelist de IP en tu proveedor de proxies) o con un proxy local como mitmproxy que inyecte las credenciales.

Patrón de rotación de proxies: IP fresca por sesión

Para scraping a escala, necesitas que cada WebDriver session use una IP diferente. El patrón es simple: una pool de proxies que rota por sesión, con sticky sessions para mantener la IP durante la vida de una sesión.

import random
import string
from seleniumwire import webdriver as sw_webdriver
from selenium.webdriver.chrome.options import Options

PROXY_HOST = "gate.proxyhat.com"
PROXY_PORT = 8080
PROXY_USER = "user-country-US"  # base username
PROXY_PASS = "PASSWORD"

def generate_session_id(length=8):
    """Genera un ID de sesión único para sticky session."""
    return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))

class RotatingProxyPool:
    """Pool de proxies residenciales con rotación por sesión."""

    def __init__(self, base_user, password, host, port, countries=None):
        self.base_user = base_user
        self.password = password
        self.host = host
        self.port = port
        self.countries = countries or ["US", "DE", "GB", "FR", "JP"]
        self._session_counter = 0

    def get_proxy_config(self, country=None, session_id=None):
        """Retorna la configuración de proxy para una nueva sesión."""
        country = country or random.choice(self.countries)
        session_id = session_id or f"sess{self._session_counter:06d}"
        self._session_counter += 1

        username = f"{self.base_user}-country-{country}-session-{session_id}"

        return {
            "proxy": {
                "http": f"http://{username}:{self.password}@{self.host}:{self.port}",
                "https": f"http://{username}:{self.password}@{self.host}:{self.port}",
            },
            "username": username,
            "session_id": session_id,
            "country": country,
        }

    def create_driver(self, country=None, headless=True):
        """Crea un WebDriver con IP fresca."""
        proxy_config = self.get_proxy_config(country=country)

        options = Options()
        if headless:
            options.add_argument("--headless=new")
        options.add_argument("--disable-blink-features=AutomationControlled")
        options.add_argument("--window-size=1920,1080")

        driver = sw_webdriver.Chrome(
            options=options,
            seleniumwire_options=proxy_config,
        )

        # Adjuntar metadata de la sesión al driver para debugging
        driver._proxy_meta = proxy_config
        return driver


# Uso
pool = RotatingProxyPool(
    base_user="user",
    password="PASSWORD",
    host=PROXY_HOST,
    port=PROXY_PORT,
    countries=["US", "DE", "GB"],
)

# Cada driver tiene su propia IP residencial
for i in range(3):
    driver = pool.create_driver(country="US")
    driver.get("https://httpbin.org/ip")
    print(f"Session {i}: {driver._proxy_meta['session_id']} -> {driver.page_source[:200]}")
    driver.quit()

Estrategias de rotación

  • Per-request: sin flag -session- — cada request obtiene una IP aleatoria del pool. Ideal para scraping masivo de SERPs donde cada request es independiente.
  • Sticky session: con -session-XXXX — la IP se mantiene durante 10-30 minutos (depende del proveedor). Necesario para flujos de login, carritos de compra o cualquier flujo multi-step.
  • Per-session: nueva IP por cada webdriver.Chrome() — el patrón de arriba. Balance entre diversidad de IPs y estabilidad dentro de una sesión.

Selenium Grid + contenedores: scraping paralelo a escala

Para procesar miles de páginas por hora, necesitas ejecutar múltiples browsers en paralelo. Selenium Grid distribuye sesiones across nodos, y Docker lo hace reproducible.

Docker Compose para Selenium Grid

# docker-compose.yml
version: '3.8'
services:
  selenium-hub:
    image: selenium/hub:4.18
    ports:
      - "4442:4442"
      - "4443:4443"
      - "4444:4444"
    environment:
      - GRID_MAX_SESSION=16
      - SE_SESSION_REQUEST_TIMEOUT=300

  chrome-node-1:
    image: selenium/node-chrome:4.18
    depends_on:
      - selenium-hub
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_MAX_SESSIONS=4
    deploy:
      resources:
        limits:
          memory: 2G

  chrome-node-2:
    image: selenium/node-chrome:4.18
    depends_on:
      - selenium-hub
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_MAX_SESSIONS=4
    deploy:
      resources:
        limits:
          memory: 2G

Cliente que conecta al Grid con proxies rotativos

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from concurrent.futures import ThreadPoolExecutor, as_completed
import requests

GRID_URL = "http://localhost:4444/wd/hub"
PROXY_HOST = "gate.proxyhat.com"
PROXY_PORT = 8080

def scrape_task(url, session_id):
    """Ejecuta una tarea de scraping en el Grid con proxy rotativo."""
    proxy_user = f"user-country-US-session-{session_id}"
    proxy_pass = "PASSWORD"

    options = Options()
    options.add_argument("--headless=new")
    options.add_argument("--proxy-server=http://gate.proxyhat.com:8080")
    # Nota: en Grid, selenium-wire no funciona directamente.
    # Usa IP whitelisting o un proxy local mitmproxy como sidecar.

    driver = webdriver.Remote(
        command_executor=GRID_URL,
        options=options,
    )

    try:
        driver.get(url)
        return {"url": url, "title": driver.title, "status": "ok"}
    except Exception as e:
        return {"url": url, "error": str(e), "status": "fail"}
    finally:
        driver.quit()


# Ejecutar 16 URLs en paralelo
urls = [f"https://httpbin.org/delay/{i % 3}" for i in range(16)]

with ThreadPoolExecutor(max_workers=8) as executor:
    futures = {
        executor.submit(scrape_task, url, f"grid{i:04d}"): url
        for i, url in enumerate(urls)
    }
    for future in as_completed(futures):
        result = future.result()
        print(result)

Patrones de escalado

  • Sidecar proxy: cada nodo del Grid ejecuta un contenedor mitmproxy como sidecar que inyecta credenciales. Chrome apunta a localhost:8080 y mitmproxy reenvía a gate.proxyhat.com:8080 con auth.
  • IP whitelisting: configura tu cuenta de ProxyHat para whitelistear las IPs de tus nodos. Así Chrome puede usar el proxy sin credenciales.
  • Auto-scaling: usa Kubernetes HPA (Horizontal Pod Autoscaler) basado en la métrica de sesiones activas del Grid para escalar nodos Chrome dinámicamente.

Comparativa: Selenium vs Playwright

Playwright nació en 2020 resolviendo muchos de los problemas que Selenium arrastra desde hace años. Pero Selenium tiene ventajas que Playwright no puede replicar fácilmente. Aquí la comparativa honesta:

Criterio Selenium Playwright
Proxy auth nativo No — requiere selenium-wire o extensiones Sí — proxy: {username, password} nativo
Stealth / anti-detección Requiere selenium-stealth o driverless Built-in: no expone navigator.webdriver
Soporte de navegadores Chrome, Firefox, Edge, Safari (legacy) Chromium, Firefox, WebKit (no Safari legacy)
Ecosistema / librerías Maduro: Appium, IDEs, CBT, miles de SO answers Creciendo rápido, pero menos recursos legacy
Grid / distribución Selenium Grid 4 + Docker, bien documentado Playwright no tiene Grid nativo — usa contenedores propios
Velocidad de ejecución Más lento (overhead de WebDriver protocol) 2-3x más rápido (CDP directo, auto-wait)
Compatibilidad legacy Excelente — funciona con tests escritos en 2015 No — API moderna, sin backward compat
Multi-tab / contexts Posible pero verbose Browser contexts nativos — aislamiento limpio

Cuándo elegir Playwright

  • Proyecto nuevo donde no tienes deuda técnica con Selenium.
  • Necesitas Selenium proxy auth sin dependencias extra — Playwright lo resuelve en 3 líneas.
  • Alto volumen con requisitos de velocidad — Playwright es significativamente más rápido.
  • Stealth es prioridad — Playwright no necesita parches para ocultar navigator.webdriver.

Cuándo quedarse con Selenium

  • Tienes cientos de tests existentes que migrar sería costoso.
  • Tu equipo ya domina Selenium y el coste de aprendizaje no se justifica.
  • Necesitas integración con herramientas que solo soportan Selenium (Appium, BrowserStack, Sauce Labs legacy).
  • Requisitos de compliance que exigen herramientas auditadas y estables.
Consejo práctico: si estás construyendo un sistema de scraping nuevo desde cero y no tienes restricciones de ecosistema, empieza con Playwright. Si mantienes una suite de QA existente o necesitas compatibilidad con herramientas de testing enterprise, Selenium sigue siendo la opción correcta.

Mejores prácticas para Selenium con proxies residenciales

  • Usa sticky sessions para flujos multi-step: login, checkout, navegación por categorías — cualquier secuencia donde el servidor pueda correlacionar IPs entre requests.
  • Rotación per-request para scraping masivo: SERPs, product pages, datos públicos — cada request es independiente y la diversidad de IPs maximiza el throughput.
  • Geo-targeting preciso: usa -country-XX-city-YYYY para simular usuarios locales. Los precios de e-commerce varían por región; los SERPs cambian por ubicación.
  • Respeta robots.txt y ToS: verifica robots.txt antes de scraping. Cumple con GDPR/CCPA para datos personales. El scraping ético reduce riesgo legal y técnico.
  • Monitorea success rate y latencia: registra cuántas requests devuelven 200 vs 403/429. Si el success rate baja del 90%, ajusta la concurrencia o rota el pool.
  • Timeouts agresivos: configura driver.set_page_load_timeout(30) y driver.implicitly_wait(10). Un navegador colgado consume recursos del Grid.
  • Limpieza de sesiones: siempre llama driver.quit() en un bloque finally. Sesiones huérfanas agotan la memoria del Grid.

Puntos clave

  • Selenium proxy auth no funciona nativamente en Chrome — usa selenium-wire para inyectar credenciales o extensiones de auto-auth en Firefox.
  • Selenium stealth reduce fingerprints pero no es infalible — selenium-driverless ofrece evasión más profunda a cambio de complejidad.
  • La rotación de IPs por sesión con sticky sessions es el patrón más equilibrado para Selenium residential proxies.
  • Selenium Grid + Docker permite paralelización, pero la autenticación proxy requiere sidecars (mitmproxy) o IP whitelisting.
  • Playwright resuelve proxy auth y stealth de forma nativa — considéralo para proyectos nuevos sin deuda técnica.
  • Siempre limpia sesiones, configura timeouts, y monitorea success rate — la higiene operativa es tan importante como la técnica.

¿Listo para llevar tu scraping al siguiente nivel con proxies residenciales confiables? Explora los planes de ProxyHat o revisa las ubicaciones disponibles para geo-targeting preciso. Para más guías técnicas, consulta nuestro artículo sobre mejores prácticas de web scraping y SERP tracking con proxies.

¿Listo para empezar?

Accede a más de 50M de IPs residenciales en más de 148 países con filtrado impulsado por IA.

Ver preciosProxies residenciales
← Volver al Blog