Playwright es una biblioteca de automatización de navegadores (Chromium, Firefox y WebKit) con API oficial para Python, Node, Java y .NET. Tiene soporte nativo para proxy con autenticación: no hace falta extensión, reenviador local ni trucos en la URL. Esta guía muestra la configuración en Python y en Node, primero para todo el navegador y después con un proxy distinto por contexto.
Antes de empezar
En el panel, en Mis proxies, copia el host, el puerto HTTP, el usuario y la contraseña. Prueba la credencial antes con curl, como explica la guía para probar el proxy: si curl falla, el problema no está en Playwright.
No escribas la credencial en el código. Los ejemplos la leen de variables de entorno:
export PROXY_SERVER="http://p1.ejemplo.com:8000"
export PROXY_USER="tu-usuario"
export PROXY_PASS="tu-contrasena"
La opción proxy
La documentación de Playwright define la opción proxy con cuatro campos:
| Campo | Qué va | Ejemplo |
|---|---|---|
server |
Esquema, host y puerto, sin credencial | http://p1.ejemplo.com:8000 |
username |
Usuario del proxy | tu-usuario |
password |
Contraseña del proxy | tu-contrasena |
bypass |
Opcional: dominios que no pasan por el proxy, separados por comas | localhost, .interno.com |
La misma opción existe en dos sitios:
- Al lanzar el navegador (
launch): todas las páginas salen por ese proxy. - Al crear un contexto (
new_contexten Python,newContexten Node): cada contexto sale por su propio proxy.
Proxy global en Python
import os
from playwright.sync_api import sync_playwright
proxy = {
"server": os.environ["PROXY_SERVER"],
"username": os.environ["PROXY_USER"],
"password": os.environ["PROXY_PASS"],
}
with sync_playwright() as p:
browser = p.chromium.launch(proxy=proxy)
page = browser.new_page()
page.goto("https://api.ipify.org")
print("IP de salida:", page.inner_text("body"))
browser.close()
Instalación: pip install playwright y después playwright install chromium.
Proxy global en Node
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({
proxy: {
server: process.env.PROXY_SERVER,
username: process.env.PROXY_USER,
password: process.env.PROXY_PASS,
},
});
const page = await browser.newPage();
await page.goto('https://api.ipify.org');
console.log('IP de salida:', await page.textContent('body'));
await browser.close();
})();
Instalación: npm install playwright y después npx playwright install chromium.
Un proxy por contexto
Un contexto es una sesión aislada dentro del mismo navegador: tiene cookies, almacenamiento local y sesión propios. Con un proxy por contexto, cada sesión sale además por su propia IP. Así puedes llevar varias cuentas en un solo proceso sin abrir un navegador por cuenta. El porqué de este patrón está en proxy por contexto en Playwright.
Python
import os
from pathlib import Path
from playwright.sync_api import sync_playwright
cuentas = [
{"nombre": "cliente-a", "proxy": {"server": "http://p1.ejemplo.com:8000",
"username": "usuario-a", "password": "contrasena-a"}},
{"nombre": "cliente-b", "proxy": {"server": "http://p2.ejemplo.com:8000",
"username": "usuario-b", "password": "contrasena-b"}},
]
Path("sesiones").mkdir(exist_ok=True)
with sync_playwright() as p:
browser = p.chromium.launch()
for cuenta in cuentas:
archivo = Path("sesiones") / f"{cuenta['nombre']}.json"
opciones = {"proxy": cuenta["proxy"]}
if archivo.exists():
opciones["storage_state"] = str(archivo)
context = browser.new_context(**opciones)
page = context.new_page()
page.goto("https://api.ipify.org")
print(cuenta["nombre"], page.inner_text("body"))
# ... aquí va el trabajo de la cuenta ...
context.storage_state(path=str(archivo))
context.close()
browser.close()
Node
const fs = require('fs');
const { chromium } = require('playwright');
const cuentas = [
{ nombre: 'cliente-a', proxy: { server: 'http://p1.ejemplo.com:8000', username: 'usuario-a', password: 'contrasena-a' } },
{ nombre: 'cliente-b', proxy: { server: 'http://p2.ejemplo.com:8000', username: 'usuario-b', password: 'contrasena-b' } },
];
(async () => {
fs.mkdirSync('sesiones', { recursive: true });
const browser = await chromium.launch();
for (const cuenta of cuentas) {
const archivo = `sesiones/${cuenta.nombre}.json`;
const context = await browser.newContext({
proxy: cuenta.proxy,
storageState: fs.existsSync(archivo) ? archivo : undefined,
});
const page = await context.newPage();
await page.goto('https://api.ipify.org');
console.log(cuenta.nombre, await page.textContent('body'));
// ... aquí va el trabajo de la cuenta ...
await context.storageState({ path: archivo });
await context.close();
}
await browser.close();
})();
storage_state / storageState guarda las cookies de cada cuenta en su propio archivo. En la siguiente ejecución, cada cuenta vuelve con su sesión y por la misma IP. En producción, mantén la relación cuenta → proxy en un archivo de configuración o en variables de entorno, no en el código.
Con Playwright Test
Si usas el ejecutor de pruebas @playwright/test, la opción proxy va en use dentro de playwright.config:
// playwright.config.js
const { defineConfig } = require('@playwright/test');
module.exports = defineConfig({
use: {
proxy: {
server: process.env.PROXY_SERVER,
username: process.env.PROXY_USER,
password: process.env.PROXY_PASS,
},
},
});
Valida la salida
Antes de iniciar sesión en cualquier cuenta, confirma la IP de cada contexto, como hacen los ejemplos con https://api.ipify.org. Lo esperado:
- cada contexto muestra la IP que contrataste para él, no la de tu máquina;
- contextos distintos muestran IPs distintas;
- la misma cuenta muestra siempre la misma IP entre ejecuciones.
Para ver también país y proveedor, cambia la URL por https://ipinfo.io/json.
Errores comunes
Credencial dentro de server. http://usuario:contraseña@host:puerto en server no es el formato de Playwright. Pon el usuario y la contraseña en username y password.
SOCKS5 con contraseña. Playwright acepta socks5:// en server, pero la autenticación está documentada para proxy HTTP. Con credencial, usa el puerto HTTP. La diferencia entre protocolos está en HTTP, HTTPS y SOCKS5.
net::ERR_PROXY_CONNECTION_FAILED. Host o puerto incorrectos, o el puerto de otro protocolo. Ver ERR_PROXY_CONNECTION_FAILED.
net::ERR_TUNNEL_CONNECTION_FAILED o respuesta 407. Normalmente, credencial incorrecta o con un espacio de más. Ver ERR_TUNNEL_CONNECTION_FAILED y error 407.
Todas las cuentas con la misma IP. El proxy está solo en launch. Para cuentas independientes, pon un proxy en cada contexto.
Sesión de una cuenta cargada en otra. Reutilizar el mismo archivo de storage_state entre cuentas mezcla cookies e identidades. Un archivo por cuenta.
Cambiar el proxy de la cuenta en cada ejecución. La cuenta pasa a conectarse desde un sitio distinto cada vez. Fija el par cuenta → IP.
Qué tipo de IP usar
- Cuentas con sesión iniciada: IPv4 dedicada, o ISP residencial si la plataforma es sensible a las IPs de datacenter. Una por contexto.
- Recopilación sin sesión: no necesitas fijar una IP por contexto; cuántas IPs dimensionar se explica en automatización con proxy: cuántas IPs.
Lo que el proxy no resuelve
El proxy cambia la IP de salida. No cambia la huella digital del navegador automatizado, no resuelve CAPTCHAs y no hace que un sitio que prohíbe la automatización pase a permitirla. Respeta los términos de cada sitio y nuestra política de uso aceptable.
Para Puppeteer y Scrapy, mira proxy en Playwright, Puppeteer y Scrapy; para Selenium, proxy en Selenium. Si todavía no tienes las IPs, mira el proxy para automatización.