---
title: "Playwright o Selenium: ¿cuál elegir en tu proyecto?"
description: "Playwright espera los elementos solo y toma el proxy por contexto; Selenium cubre más lenguajes y Safari real. Escribimos el mismo trabajo con las dos."
url: https://proxynet.io/es/blog/playwright-vs-selenium
date: 2026-09-19
author: "Acar Diveroli"
category: "Comparativas, Web scraping"
lang: es
---

# Playwright o Selenium: ¿cuál elegir en tu proyecto?

Empiezas un proyecto nuevo de recolección de datos o de pruebas y la página se construye con JavaScript, así que necesitas un navegador de verdad. En el equipo hay alguien que lleva años escribiendo Selenium y la desarrolladora nueva propone Playwright. Las dos abren el navegador desde el código y hacen clic, las dos son gratuitas; ese parecido es justo lo que complica la elección. La mayoría de las comparaciones mira el asunto con ojos de QA y se salta los apartados que deciden el resultado en la recolección de datos, como los proxies y la espera.

En este artículo comparamos las dos herramientas en siete ejes: arquitectura, espera automática, gestión del proxy, soporte de lenguajes, instalación del navegador, paralelismo y depuración. El código que hace el mismo trabajo pequeño con las dos lo ejecutamos con Playwright 1.63, Selenium 4.49 y Chrome 153 en Windows 11, y contamos lo que vimos en cada apartado. "Cuál se nota menos" no es el tema de este artículo: las dos abren un navegador real y en las dos te toca a ti respetar las reglas del sitio.

> **Nota: Respuesta breve**
>
> Playwright habla con el navegador por una conexión permanente, espera solo hasta que el elemento está listo y toma el proxy con usuario y contraseña para cada contexto por separado; en un proyecto nuevo llegas al resultado con menos código. Selenium sigue el estándar W3C WebDriver, soporta más lenguajes, incluido Ruby, y el Safari real, y se reparte entre máquinas con Grid; si tienes una suite de Selenium en marcha o un equipo volcado en Java y C#, casi nunca hay motivo para dejarla. La autenticación del proxy no forma parte del estándar en Selenium y el camino práctico es la lista blanca de IP.

## ¿Qué son Playwright y Selenium?

Selenium es un proyecto de automatización de navegador de código abierto que se desarrolla desde 2004. La parte que se usa hoy es Selenium WebDriver: tu código envía órdenes a un driver de navegador y el driver maneja el navegador. La instalación la cubre nuestro artículo [Selenium con proxy](/es/blog/selenium); la descarga del driver y la autenticación están explicadas allí.

Playwright es una biblioteca de automatización de navegador de código abierto que Microsoft publicó en 2020. Maneja los motores Chromium, Firefox y WebKit con una sola API. Su instalación y sus ajustes de proxy están en [Qué es Playwright y cómo usarlo con un proxy](/es/blog/playwright-proxy); aquí tratamos solo los puntos en los que se separa de Selenium.

Las dos responden a la misma necesidad: contenido que no llega con una petición HTTP simple y que aparece cuando el JavaScript corre en el navegador. Si la página necesita de verdad un navegador lo averiguas con la comprobación de [Páginas estáticas y dinámicas en web scraping](/es/blog/static-vs-dynamic-pages). Si el dato está en el código fuente de la página, un cliente HTTP simple o [Scrapy](/es/blog/scrapy-proxy) sale más barato que cualquiera de las dos.

## Cuál es la diferencia de arquitectura: WebDriver, BiDi y CDP

Casi toda la diferencia de comportamiento entre las dos herramientas nace de cómo hablan con el navegador.

En Selenium el camino de una orden es este:

1. Tu código llama a `driver.find_element(...)`.
2. La biblioteca de Selenium lo convierte en una petición HTTP del estándar W3C WebDriver.
3. La petición va al programa driver que escribe el fabricante del navegador: ChromeDriver para Chrome, GeckoDriver para Firefox.
4. El driver aplica la orden al navegador y devuelve el resultado como respuesta HTTP.

Cada orden es una petición y una respuesta aparte; el navegador no puede contarte nada por su cuenta. Un error en la consola o una petición de red que sale en segundo plano queda invisible mientras no preguntes. Para cerrar esa carencia, el proyecto Selenium escribe junto con los fabricantes de navegadores el estándar [WebDriver BiDi](https://www.w3.org/TR/webdriver-bidi/): un protocolo bidireccional sobre WebSocket. En Selenium 4 se activa con `options.enable_bidi = True`; como la transición sigue en curso, hoy conviven dos mundos en Selenium.

En Playwright el camino es distinto:

1. Tu código llama a `page.locator(...).click()`.
2. La biblioteca de Python, Java o .NET pasa esa llamada al driver de Playwright incluido en el paquete. El driver es un proceso de Node.js; en el entorno virtual está como `playwright/driver/node.exe`.
3. El driver habla con el navegador por una conexión permanente. En Chromium es el Chrome DevTools Protocol (CDP). Para Firefox y WebKit, Playwright usa sus propias compilaciones parcheadas.
4. Como la conexión es bidireccional, los eventos del navegador (petición, respuesta, mensaje de consola, descarga) llegan a tu código sin que los pidas.

Esa elección tiene su precio. El [documento de navegadores de Playwright](https://playwright.dev/python/docs/browsers) lo dice claro: como se apoya en parches, no funciona con el Firefox ni el Safari de marca. Chrome y Edge sí los puedes usar con la opción `channel`, pero una compilación de WebKit no cubre la necesidad de "probar en Safari real"; Selenium maneja el Safari instalado con el driver de Apple.

## Tabla comparativa

| | Playwright | Selenium |
|---|---|---|
| Protocolo | Conexión permanente; CDP en Chromium, compilaciones parcheadas en Firefox y WebKit | W3C WebDriver (HTTP), junto al naciente WebDriver BiDi (WebSocket) |
| Capa intermedia | Driver de Playwright incluido en el paquete | Driver del fabricante del navegador (ChromeDriver, GeckoDriver) |
| Navegadores | Chromium, Firefox, WebKit; Chrome y Edge con `channel` | Chrome, Edge, Firefox, Safari |
| Lenguajes oficiales | JavaScript y TypeScript, Python, Java, .NET | Java, Python, C#, Ruby, JavaScript |
| Espera | Automática antes de las acciones | La escribes tú: `WebDriverWait` |
| Alcance del proxy | Por navegador o por contexto | Por sesión de navegador |
| Usuario y contraseña del proxy | Campos propios en el objeto `proxy` | No hay campo en el estándar; el camino práctico es la lista blanca de IP |
| Escuchar y bloquear peticiones | Integrado (`route`, `expect_response`) | Llega con BiDi; no existe en el WebDriver clásico |
| Instalación del navegador | `playwright install`, compilaciones fijadas a la versión | Selenium Manager descarga el driver solo y usa el navegador instalado |
| Paralelismo | Muchos contextos en un navegador | Un navegador por tarea; Selenium Grid entre máquinas |
| Depuración | Trace Viewer, Inspector, codegen | Captura de pantalla, registro del navegador, Selenium IDE |

## ¿Cómo se escribe el mismo trabajo con las dos herramientas?

Nuestra página de prueba es `quotes.toscrape.com/js-delayed/`: un sitio de práctica que imprime sus citas con JavaScript y diez segundos de retraso. El trabajo: abrir la página por un proxy, leer las citas, hacer clic en el enlace "Next" y comprobar la segunda página.

Con Playwright:

```python
from playwright.sync_api import sync_playwright

URL = "https://quotes.toscrape.com/js-delayed/"
PROXY = {
    "server": "http://pr.proxynet.io:8000",
    "username": "user",
    "password": "pass",
}

with sync_playwright() as p:
    browser = p.chromium.launch(proxy=PROXY)
    page = browser.new_page()
    page.goto(URL)
    quotes = page.locator("div.quote")
    print("immediately:", quotes.count())  # 0: count() no espera
    quotes.first.wait_for()  # espera hasta que la primera cita esté en la página
    for quote in quotes.all():
        text = quote.locator("span.text").inner_text()
        author = quote.locator("small.author").inner_text()
        print(author, "-", text[:50])
    page.get_by_role("link", name="Next").click()  # espera sola antes del clic
    page.wait_for_url("**/page/2/")
    print(page.url)
    browser.close()
```

Con Selenium:

```python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

URL = "https://quotes.toscrape.com/js-delayed/"

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
# Sin usuario ni contraseña: tu IP de salida debe estar en la lista blanca del panel
options.add_argument("--proxy-server=http://pr.proxynet.io:8000")

driver = webdriver.Chrome(options=options)  # el driver lo encuentra Selenium Manager
try:
    driver.get(URL)
    print("immediately:", len(driver.find_elements(By.CSS_SELECTOR, "div.quote")))  # 0
    wait = WebDriverWait(driver, 15)
    quotes = wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "div.quote")))
    for quote in quotes:
        text = quote.find_element(By.CSS_SELECTOR, "span.text").text
        author = quote.find_element(By.CSS_SELECTOR, "small.author").text
        print(author, "-", text[:50])
    wait.until(EC.element_to_be_clickable((By.PARTIAL_LINK_TEXT, "Next"))).click()
    wait.until(EC.url_contains("/page/2/"))
    print(driver.current_url)
finally:
    driver.quit()
```

Los dos scripts imprimieron las mismas diez citas a través de nuestros proxies de prueba locales y pasaron a `/js-delayed/page/2/`. El número de líneas es parecido; la diferencia está en el detalle. En Playwright las credenciales del proxy van dentro del código y en Selenium no; el clic en Playwright es una línea y en Selenium la condición de que el elemento sea clicable se escribe a mano. En los dos scripts la línea "immediately" devolvió `0`: la página cuenta como cargada, pero el contenido todavía no está. De eso trata el apartado siguiente.

## ¿Qué diferencia hay entre la espera automática y WebDriverWait?

En las páginas dinámicas, la mayoría de los fallos sale del momento en que ocurren las cosas: el código busca un elemento que JavaScript aún no ha impreso. El [documento de esperas de Selenium](https://www.selenium.dev/documentation/webdriver/waits/) lo llama condición de carrera; a veces el navegador llega antes y el código funciona, a veces el código se adelanta y recibes un error.

En Selenium la solución está en tus manos. `driver.get()` espera al evento `load` de la página, pero no sabe nada del contenido que JavaScript añade después. El valor por defecto de la espera implícita es cero; si el elemento no está, el error vuelve enseguida. El camino recomendado es la espera explícita: `WebDriverWait` consulta la página hasta que una condición se cumple. El documento añade otro aviso: no uses las dos juntas, porque los tiempos se vuelven impredecibles.

Playwright mete ese mismo trabajo dentro de la acción. Según el [documento de actionability](https://playwright.dev/python/docs/actionability), una llamada a `click()` espera a que el elemento sea visible, su posición esté estable, pueda recibir el clic y esté activo; si las condiciones no se cumplen dentro del plazo, lanza un `TimeoutError`. Las llamadas que trabajan con un solo elemento, como `fill()` e `inner_text()`, también esperan a que el elemento aparezca.

Esto tiene un límite: la espera automática es para las acciones, no para los recuentos. En el ejemplo de arriba `quotes.count()` devolvió `0` sin esperar. El documento de la API de Playwright deja la misma nota para `locator.all()`: no espera a los elementos que coinciden, te da lo que hay en la página en ese momento. Si vas a leer una lista, espera antes al primer elemento con `first.wait_for()`. Por eso la frase "en Playwright no se escriben esperas" solo es medio cierta.

La segunda diferencia está en la referencia al elemento. En Selenium, `find_element` devuelve una referencia al nodo del DOM tal como está; si la página redibuja esa parte, la referencia caduca y recibes una `StaleElementReferenceException`. En Playwright un `locator` es una receta que se resuelve de nuevo en cada uso, y ese error apenas aparece. El lenguaje de selectores queda de tu lado en las dos herramientas: [Selector CSS o XPath: ¿cuál usar en web scraping?](/es/blog/css-selector-vs-xpath).

## ¿En qué se diferencia la gestión del proxy?

La parte de Playwright la contamos en el artículo hermano, aquí va el resumen: el objeto `proxy` se pasa a `launch()` o a `new_context()`, `username` y `password` son campos aparte y en un solo navegador cada contexto puede salir por una IP distinta. Un contexto aísla las cookies, el almacenamiento y el proxy. Como el navegador y la máquina siguen siendo los mismos, la [huella del navegador](/es/blog/browser-fingerprinting) no cambia entre contextos; un contexto es un separador de sesiones, no un dispositivo aparte.

En Selenium el proxy es una capacidad de sesión: se entrega al arrancar el navegador y ata todas las pestañas de ese navegador; para otro proxy lo cierras con `driver.quit()` y abres un navegador nuevo. La parte de autenticación falta en el estándar mismo. [La definición de proxy del W3C WebDriver](https://www.w3.org/TR/webdriver2/#proxy) enumera las claves `httpProxy`, `sslProxy`, `socksProxy`, `socksVersion` y `noProxy`; no hay clave para usuario ni contraseña. El ejemplo oficial de Selenium también muestra solo la forma `<HOST:PORT>`. El estándar dice que la dirección del servidor puede llevar credenciales, pero el [documento de proxy de Chromium](https://chromium.googlesource.com/chromium/src/+/HEAD/net/docs/proxy.md#Proxy-credentials-in-manual-proxy-settings) escribe que Chrome no usa las credenciales incrustadas en los ajustes del proxy.

Probamos a qué lleva esto en la práctica con un proxy local que pide autenticación. Los resultados corresponden a ejecuciones únicas en esta máquina (Selenium 4.49, Chrome 153):

| Lo que probamos | Lo que pasó |
|---|---|
| `--proxy-server=http://servidor:puerto`, sin credenciales | El proxy devolvió `407`. `driver.get()` no lanzó error; la página quedó vacía y `driver.title` volvió vacío |
| `--proxy-server=http://user:pass@servidor:puerto` | Chrome abrió la página de error `ERR_NO_SUPPORTED_PROXIES`, otra vez sin excepción |
| `user:pass@servidor:puerto` en el objeto `Proxy` | La página se abrió, pero al registro del proxy no llegó ni una petición: el navegador conectó directo, con nuestra propia IP |
| Puerto que no pide autenticación (el equivalente de la lista blanca de IP) | Funcionó sin problemas |

La tercera fila es la más peligrosa: el script parece funcionar cuando el tráfico no pasa por el proxy. Tampoco existe un argumento de Chrome llamado `--proxy-auth`, que aparece en artículos antiguos. La llamada `add_auth_handler` del lado BiDi de Selenium se explica en la documentación para la autenticación Basic del propio sitio; no es un camino documentado para proxies, y nuestra prueba con un proxy definido acabó en tiempo de espera agotado al cargar la página.

Por eso el camino que recomendamos en Selenium es la lista blanca de IP: añades la IP de salida del servidor donde corre el script a la lista del panel y das el valor de `--proxy-server` sin credenciales. La comparación de los dos métodos está en [Autenticación de proxy: user:pass o lista blanca de IP](/es/blog/proxy-authentication-methods), y la sintaxis de proxy de SeleniumBase en [Proxy con SeleniumBase: autenticación y rotación](/es/blog/how-to-use-proxy-with-seleniumbase).

En esas mismas ejecuciones hicimos otra observación: el Chrome instalado que abrió Selenium conectó por el proxy también con los servicios de actualización y de cuenta de Google, además del sitio objetivo; en la compilación propia de Chromium de Playwright al registro del proxy solo llegaron las peticiones de la página. Cuando pagas el tráfico por gigabyte con [Proxies residenciales](https://proxynet.io/es/residential-proxy), merece la pena vigilar ese tráfico de fondo desde tu panel.

## Soporte de lenguajes y ecosistema

Las bibliotecas oficiales de Selenium son para Java, Python, C#, Ruby y JavaScript. Las de Playwright, para JavaScript y TypeScript, Python, Java y .NET. Si eres un equipo de Ruby, la elección se hace sola.

Aunque sobre el papel los lenguajes se solapen, los centros de gravedad son distintos. Selenium es un proyecto de veinte años; buena parte de los equipos de pruebas corporativos escribe Selenium en Java o C#, hay suites de cientos de escenarios montadas con TestNG, JUnit, NUnit y Cucumber, y reescribirlas cuesta casi siempre más de lo que aporta la comodidad de Playwright. La cara más madura de Playwright es la de Node.js: su propio ejecutor de pruebas llega con ejecución en paralelo, comparación de capturas y grabación automática de trazas. En Python el camino recomendado es el plugin de pytest. La recolección de datos con C# la tratamos en [Extraer datos de un sitio web con C#: HttpClient y proxy](/es/blog/csharp-web-scraping).

En scraping, la elección del lenguaje suele ir por delante de la de la herramienta: si la cadena que procesa los datos está en Python, las dos sirven; si está en Node.js, Playwright encaja con más naturalidad. La comparación de los dos lenguajes está en [Web scraping: ¿JavaScript o Python?](/es/blog/web-scraping-javascript-vs-python).

## Instalación del navegador: Selenium Manager y playwright install

Las guías viejas de Selenium te dicen que descargues a mano el ChromeDriver que corresponde a tu versión de Chrome y que des su ruta con `executable_path`. Las dos cosas quedaron atrás: en Selenium 4.49, `webdriver.Chrome()` solo acepta los parámetros `options`, `service` y `keep_alive`, y desde la versión 4.6 encontrar el driver es tarea de [Selenium Manager](https://www.selenium.dev/documentation/selenium_manager/), que viene con la biblioteca: detecta la versión del navegador instalado, descarga el driver que encaja y lo guarda en la carpeta `~/.cache/selenium`. En nuestra prueba descargó el ChromeDriver 153 para el Chrome 153 instalado sin ningún paso extra. Según el documento, también puede descargar Chrome, Firefox y Edge si el navegador no está instalado; la herramienta se sigue versionando como beta.

El planteamiento de Playwright es el contrario: no se fía del navegador del sistema. `playwright install chromium` descarga su propia compilación y cada versión de Playwright queda fijada a una compilación concreta del navegador. Si actualizas la biblioteca y olvidas volver a ejecutar `install`, recibes el error "Executable doesn't exist"; a nosotros nos pasó en las pruebas de este artículo.

El intercambio es este: Selenium conduce el navegador que tus usuarios usan de verdad y que se actualiza solo, lo que tiene sentido para probar, pero el comportamiento puede cambiar cuando el navegador se actualiza. En Playwright la versión del navegador queda fijada con el código; la actualización la eliges tú.

## Trabajo en paralelo: ¿Grid o contextos?

En Playwright la unidad del paralelismo es el contexto. Se abre un proceso de navegador y cada tarea toma su contexto y, si quieres, su propio proxy:

```python
import asyncio

from playwright.async_api import async_playwright

URLS = [f"https://quotes.toscrape.com/js/page/{n}/" for n in range(1, 4)]
PROXY = {"server": "http://pr.proxynet.io:8000", "username": "user", "password": "pass"}

async def scrape(browser, url):
    context = await browser.new_context(proxy=PROXY)  # sesión aislada con su propio proxy
    try:
        page = await context.new_page()
        await page.goto(url)
        quotes = page.locator("div.quote span.text")
        await quotes.first.wait_for()
        return url, await quotes.count()
    finally:
        await context.close()

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch()  # un solo proceso de navegador
        for url, count in await asyncio.gather(*(scrape(browser, u) for u in URLS)):
            print(url, count)
        await browser.close()

asyncio.run(main())
```

En Selenium la unidad es el navegador mismo. El mismo trabajo se escribe con un grupo de hilos que abre un driver por tarea:

```python
from concurrent.futures import ThreadPoolExecutor

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

URLS = [f"https://quotes.toscrape.com/js/page/{n}/" for n in range(1, 4)]

def scrape(url):
    options = webdriver.ChromeOptions()
    options.add_argument("--headless=new")
    options.add_argument("--proxy-server=http://pr.proxynet.io:8000")  # con lista blanca de IP
    driver = webdriver.Chrome(options=options)  # un proceso de navegador por tarea
    try:
        driver.get(url)
        quotes = WebDriverWait(driver, 15).until(
            EC.presence_of_all_elements_located((By.CSS_SELECTOR, "div.quote span.text"))
        )
        return url, len(quotes)
    finally:
        driver.quit()

with ThreadPoolExecutor(max_workers=3) as pool:
    for url, count in pool.map(scrape, URLS):
        print(url, count)
```

Los dos scripts devolvieron diez citas de cada una de las tres páginas. La diferencia está en el consumo de recursos: en el primero se abre un navegador, en el segundo tres navegadores y tres procesos de driver. Con veinte sesiones simultáneas esa diferencia se nota directamente en la memoria. Cuándo sirven la concurrencia y el paralelismo en scraping está en [Concurrencia y paralelismo en web scraping](/es/blog/concurrency-vs-parallelism).

Donde Selenium es fuerte es en el reparto entre máquinas. Selenium Grid dirige las órdenes a navegadores de máquinas remotas; según su documento oficial, su objetivo es ejecutar pruebas en paralelo en más de una máquina. Correr la matriz "Edge en Windows, Safari en macOS, Firefox en Linux" desde una sola suite es tarea de Grid. Playwright no tiene un equivalente directo: el reparto entre máquinas lo deja a tu sistema de CI. Su capacidad de conectarse a Grid está marcada como experimental en la documentación y cubre solo los navegadores basados en Chromium.

## Herramientas de depuración

En este apartado Playwright va claramente por delante. Una grabación que empieza con `context.tracing.start(screenshots=True, snapshots=True)` y termina con `tracing.stop(path="trace.zip")` se abre con el comando `playwright show-trace trace.zip`: la vista del DOM antes y después de cada acción, las peticiones de red y los mensajes de consola quedan en una línea de tiempo. Por qué un rastreo que corrió de noche volvió vacío lo respondes por la mañana desde ese archivo. `PWDEBUG=1` abre el Inspector y recorre el script paso a paso, y `playwright codegen` convierte en código lo que haces en el navegador.

En Selenium no hay una herramienta equivalente única. Con `driver.save_screenshot()` tomas una captura y en Chrome activas la capacidad `goog:loggingPrefs` y lees los mensajes de consola con `driver.get_log("browser")`; probamos las dos cosas y funcionan. La necesidad de grabar y reproducir la cubre la extensión Selenium IDE. Escuchar las peticiones de red en vivo llega con BiDi, pero para un registro que recorres después te apoyas en marcos de reporting.

## Velocidad: ¿por qué no damos cifras?

La mayoría de las comparaciones de internet lleva un porcentaje del tipo "Playwright es tanto más rápido"; esas cifras se tomaron en la máquina del autor y en su propia página. La arquitectura crea una expectativa a favor de Playwright: una conexión abierta en lugar de una petición HTTP por orden, un contexto en lugar de un navegador. Pero en un rastreo real que pasa por un proxy, la mayor parte del tiempo se va en la red y en el tiempo de respuesta del sitio objetivo; en nuestro ejemplo los dos scripts terminaron a la sombra de los diez segundos de retraso de la página.

Si vas a medir, mide en tu propio objetivo, con el mismo proxy y la misma concurrencia, con una muestra de algunas decenas de páginas. En la mayoría de los proyectos la ganancia real no viene de cambiar de herramienta, sino de no abrir el navegador cuando no hace falta.

## Casos de uso

- **Recoger datos de páginas dinámicas:** en un proyecto nuevo, escuchar peticiones y bloquear recursos en Playwright reduce el tráfico; el planteamiento general está en la página de [solución de extracción de datos](/es/data-scraping).
- **Rastrear muchas páginas con regularidad:** la lógica de cola y descubrimiento no depende de la herramienta; mira la página de [solución de web crawler](/es/web-crawler) y el artículo [Qué es la paginación y cómo se rastrea en scraping](/es/blog/pagination-web-scraping).
- **Probar tu aplicación desde distintos países:** en Playwright un contexto por país, en Selenium una sesión por país; el detalle está en la página de [pruebas de aplicaciones](/es/app-testing).
- **Prueba de compatibilidad entre navegadores:** si necesitas Safari real y una matriz de sistemas operativos, Selenium y Grid.
- **Rastreo masivo de páginas independientes:** en las dos herramientas basta una sola dirección de pasarela con [Proxies rotativos](https://proxynet.io/es/rotating-proxy); de la rotación se encarga la pasarela.

Sea cual sea la herramienta, el marco no cambia: respeta las reglas de `robots.txt` y los términos de uso, usa la API oficial si la hay y mantén el ritmo de peticiones en un nivel que el sitio aguante. El detalle está en [Qué es robots.txt y cómo leerlo](/es/blog/robots-txt).

## Errores frecuentes

- **Incrustar las credenciales en la dirección del proxy en Selenium.** Chrome no las usa; en nuestra prueba el resultado fue o una página de error o una conexión directa sin proxy.
- **Dar por hecho que la página se abrió porque `driver.get()` no dio error.** Selenium no te entrega el código de estado; comprueba el elemento que esperas y la IP de salida.
- **Creer que `count()` o `all()` van a esperar en Playwright.** No esperan; primero `first.wait_for()`.
- **Resolver la espera con `time.sleep()`.** Es lento y frágil a la vez; usa `WebDriverWait` en Selenium y la espera del locator en Playwright.
- **Mezclar la espera implícita y la explícita en Selenium.** El documento oficial avisa: los tiempos se vuelven impredecibles.
- **No llamar a `driver.quit()` en Selenium.** Cada sesión sin cerrar deja un proceso de Chrome y uno de driver; usa `try/finally`.
- **Mover una suite de Selenium que funciona solo por moda.** El coste de la migración no está en los selectores, sino en la lógica de espera y en la infraestructura de pruebas.
- **Actualizar Playwright y no actualizar los navegadores.** Cada versión quiere su compilación; añade un paso `playwright install` a tu imagen de CI.

## Guía de decisión

| Necesidad | Recomendación |
|---|---|
| Proyecto de scraping que empieza, en Python o Node.js | Playwright |
| Proxy que funciona con usuario y contraseña | Playwright; en Selenium pasa a la lista blanca de IP |
| Decenas de sesiones independientes en una máquina, una IP por sesión | Playwright, proxy por contexto |
| Suite de pruebas de Selenium en marcha y mantenida | Quédate en Selenium |
| Equipo de pruebas corporativo volcado en Java o C# | Sirven las dos; deciden el marco existente y la experiencia |
| Ruby | Selenium |
| Pruebas en Safari real y en distintos sistemas operativos | Selenium y Grid |
| Depurar después en trabajos que corren de noche | Playwright, Trace Viewer |
| Leer la respuesta de red, bloquear imágenes y fuentes | Playwright (integrado) |
| Dato en el código fuente de la página o en un endpoint JSON | Ninguna de las dos: cliente HTTP simple o Scrapy |

## Preguntas frecuentes

### ¿Playwright sustituye a Selenium?

En los proyectos nuevos es a menudo la primera opción, pero decir que lo ha sustituido no sería correcto. Selenium es la implementación de referencia de un estándar del W3C, sus drivers los escriben los fabricantes de navegadores y la comunicación bidireccional que faltaba se añade por la vía del estándar con WebDriver BiDi. Los dos proyectos se desarrollan de forma activa.

### ¿Es difícil pasar de Selenium a Playwright?

Los selectores se llevan en buena medida; CSS y XPath funcionan en las dos. La parte que cuesta es la lógica de espera: los bloques de `WebDriverWait` desaparecen y en su lugar llega un flujo basado en locators; del lado de las pruebas, los objetos de página, el reporting y los pasos de CI también se vuelven a montar. Un script pequeño se traslada en un día; en una suite de cientos de escenarios es menos arriesgado escribir primero los escenarios nuevos en Playwright y dejar los viejos donde están.

### ¿Se pueden usar las dos en el mismo proyecto?

Sí, no se estorban. La disposición habitual es conservar la suite de regresión de Selenium y escribir el trabajo nuevo en Playwright. Lo que hay que cuidar es gestionar juntos dos modelos de instalación de navegador en la imagen de CI.

### ¿En Selenium no se puede usar nunca un proxy con usuario y contraseña?

Por la vía estándar no: la definición de proxy del WebDriver no tiene campo de credenciales y Chrome no usa las credenciales incrustadas en la dirección. Los paquetes de terceros que ponen en medio un proxy local de reenvío llenan ese hueco, pero añaden una dependencia. Si trabajas desde un servidor con IP fija, la lista blanca de IP es más sencilla y más segura; la contraseña no aparece en el código.

### ¿Selenium en Python es más lento que Playwright?

No hay respuesta general. La arquitectura favorece a Playwright, pero en un rastreo que pasa por un proxy el tiempo lo deciden sobre todo la red y el sitio objetivo. No traslades a tu trabajo los porcentajes publicados; mide tú en el mismo objetivo, con el mismo proxy y la misma concurrencia.

### ¿Se comportan distinto las dos en modo headless?

Las dos funcionan sin ventana. Playwright arranca headless por defecto y descarga para ello una compilación aparte llamada "headless shell"; para ver la ventana escribes `launch(headless=False)`. Selenium arranca con ventana por defecto y pasa al modo sin ventana en Chrome con el argumento `--headless=new`. El ajuste del proxy es el mismo en los dos modos.

## En resumen

Playwright y Selenium hacen el mismo trabajo por caminos distintos. Selenium envía las órdenes por W3C WebDriver al driver del fabricante del navegador; cubre más lenguajes, el Safari real y el reparto entre máquinas con Grid, y te deja a ti la espera y las credenciales del proxy. Playwright establece una conexión permanente con el navegador; espera solo, toma el proxy con usuario y contraseña por contexto, escucha el tráfico de red y facilita la depuración con un archivo de traza. En un proyecto nuevo de scraping, Playwright te hace avanzar con menos código; si tienes una suite de Selenium en marcha, casi siempre basta con atarla al proxy mediante una lista blanca de IP. Los tipos de proxy que puedes usar con las dos herramientas están en nuestros [servicios de proxy](/es/proxy).
