Si alguna vez has mantenido dos codebases separados —uno con requests para páginas estáticas y otro con Playwright o Selenium para contenido renderizado por JavaScript— sabes lo costoso que es duplicar lógica de selectores, gestión de cookies y rotación de proxies. DrissionPage resuelve exactamente ese problema: un único framework Python que alterna entre HTTP puro y control de Chromium vía CDP, compartiendo cookies y sesión entre ambos modos.
En esta guía implementaremos DrissionPage con proxies residenciales de ProxyHat, cubriendo la API idiomática, la configuración de proxies en cada modo y patrones de producción para scraping a escala.
Aviso legal y ético: Esta guía asume que extraes datos públicos, respetas
robots.txt, los términos de servicio del sitio y normativas como el RGPD (Reglamento 2016/679) y la CFAA en EE. UU. Si un sitio ofrece una API oficial, úsala primero. El scraping no autorizado de datos personales puede ser ilegal.
El modelo de DrissionPage: SessionPage, ChromiumPage y WebPage
DrissionPage (documentación oficial) se basa en tres clases principales que representan distintos niveles de interacción con una página web:
| Clase | Motor | Cuándo usar | Coste relativo |
|---|---|---|---|
SessionPage | HTTP (estilo requests, basado en urllib3) | Páginas HTML estáticas, APIs JSON, descargas de archivos | Bajo (~50 ms por request) |
ChromiumPage | Navegador real vía CDP (Chrome DevTools Protocol) | SPAs, contenido cargado por JS, interacciones complejas | Alto (~300–800 ms por navegación) |
WebPage | Conmuta entre SessionPage y ChromiumPage | Cuando necesitas ambos modos en el mismo flujo | Variable según modo activo |
La clave del ahorro está en WebPage: puedes iniciar en modo HTTP para la mayoría de requests y escalar a navegador solo cuando la página lo exige, sin perder cookies ni headers. En un pipeline típico de scraping de e-commerce, el 80% de las páginas de producto son HTML estático; solo el 20% requiere renderizado JS. Con DrissionPage evitas arrancar Chromium para el 80%, reduciendo el coste de cómputo y memoria de forma significativa.
API idiomática de DrissionPage: selectores, opciones y captura de paquetes
Selectores con ele() y eles()
DrissionPage unifica la sintaxis de localización de elementos. El método ele() devuelve el primer elemento que coincide; eles() devuelve una lista. La sintaxis soporta varios formatos:
from DrissionPage import WebPage
page = WebPage()
page.get('https://example.com/products')
# Por texto de atributo
precio = page.ele('@class=price').text
# Por etiqueta + atributo
inputs = page.eles('tag:input')
# XPath
titulo = page.ele('xpath://h1[@id="main-title"]').text
# Texto parcial
enlace = page.ele('texto:Comprar ahora')
Esta sintaxis funciona igual en SessionPage y ChromiumPage, lo que significa que puedes cambiar de modo sin reescribir tus selectores.
ChromiumOptions para configurar el navegador
ChromiumOptions controla cómo se lanza Chromium: argumentos, ruta del binario, modo headless, proxy, user-agent y más.
from DrissionPage import ChromiumOptions
co = ChromiumOptions()
co.headless(True)
co.set_argument('--no-sandbox')
co.set_argument('--disable-gpu')
co.set_user_agent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...')
co.set_proxy('http://gate.proxyhat.com:8080')
Captura de paquetes con listen.start()
Una de las funciones más potentes de DrissionPage es listen, que intercepta tráfico de red del navegador para capturar respuestas XHR/fetch en segundo plano. Esto permite descubrir APIs ocultas que devuelven JSON limpio, evitando parsear HTML renderizado:
page = ChromiumPage()
page.listen.start('api/products') # filtra URLs que contengan este patrón
page.get('https://example.com/catalog')
packet = page.listen.wait() # bloquea hasta capturar el primer paquete
print(packet.url) # URL completa del request
print(packet.response.body) # cuerpo de la respuesta (JSON)
Este patrón es especialmente útil para sitios que cargan datos vía XHR: en lugar de esperar a que el DOM se renderice y extraer elementos, capturas el JSON directamente, reduciendo el tiempo de procesamiento y la fragilidad del selector.
Configurar un proxy en DrissionPage: SessionPage y ChromiumPage
El término drissionpage proxy se refiere a la configuración de proxies en ambos modos del framework. La configuración difiere según la clase:
Proxy en SessionPage (HTTP)
SessionPage usa un diccionario de proxies compatible con el formato de requests:
from DrissionPage import SessionPage
sp = SessionPage()
sp.set.proxies({
'http': 'http://user-country-US:pass@gate.proxyhat.com:8080',
'https': 'http://user-country-US:pass@gate.proxyhat.com:8080',
})
sp.get('https://httpbin.org/ip')
print(sp.html)
Proxy en ChromiumPage (navegador)
Para el navegador, el proxy se configura en ChromiumOptions antes de lanzar la instancia:
from DrissionPage import ChromiumPage, ChromiumOptions
co = ChromiumOptions()
co.set_proxy('http://user-country-US:pass@gate.proxyhat.com:8080')
page = ChromiumPage(co)
page.get('https://httpbin.org/ip')
print(page.ele('tag:pre').text)
Los proxies residenciales son necesarios para objetivos con protección anti-bot avanzada (Cloudflare, PerimeterX, DataDome) porque los IPs de datacenter son detectados y bloqueados en cuestión de minutos. Un IP residencial rotativo se comporta como un usuario real desde un ISP doméstico, reduciendo la tasa de bloqueo del 90% al 5–15% según el objetivo.
Ejemplo completo: WebPage con proxy residencial de ProxyHat
A continuación, un ejemplo runnable que inicia en modo HTTP con un proxy residencial de EE. UU. y sesión pegajosa, luego escala a ChromiumPage para una página que requiere JavaScript:
from DrissionPage import WebPage
import hashlib, time
def build_username(country='US', session_id=None):
"""Construye el username de ProxyHat con geo-targeting y sesión."""
user = f'user-country-{country}'
if session_id:
user += f'-session-{session_id}'
return user
# Construir URL del proxy residencial
session_id = hashlib.md5(str(time.time()).encode()).hexdigest()[:8]
username = build_username(country='US', session_id=session_id)
password = 'tu_password'
proxy_url = f'http://{username}:{password}@gate.proxyhat.com:8080'
# Iniciar WebPage en modo HTTP
page = WebPage(mode='session')
page.set.proxies({
'http': proxy_url,
'https': proxy_url,
})
# Fase 1: HTTP rápido para la página de listado
page.get('https://example.com/products/category')
product_links = page.eles('xpath://a[contains(@href, "/product/")]')
urls = [link.attr('href') for link in product_links[:10]]
print(f'Encontrados {len(urls)} productos')
# Fase 2: escalar a Chromium para una página que requiere JS
page.change_mode() # cambia a ChromiumPage, conserva cookies
# El proxy del navegador se hereda de la configuración de ChromiumOptions
# o se puede setear explícitamente antes del change_mode
for url in urls[:3]:
page.get(url)
# Esperar a que el precio dinámico cargue
precio = page.ele('@class=dynamic-price', timeout=5)
if precio:
print(f'{url} -> {precio.text}')
time.sleep(2) # rate limiting educado
page.close()
Este patrón —HTTP primero, navegador solo cuando es necesario— es la esencia del drissionpage web scraping eficiente. En benchmarks típicos, procesar 1000 páginas de producto en modo HTTP toma ~50 segundos; las mismas 1000 páginas en Chromium toman ~10 minutos. La diferencia se traduce directamente en coste de infraestructura.
Para más detalles sobre las ubicaciones disponibles para geo-targeting, consulta nuestra página de ubicaciones de ProxyHat.
Patrones de producción: sesiones pegajosas, reintentos y concurrencia
Sesión pegajosa por proxy
Cuando un sitio requiere login o mantiene estado por sesión, necesitas que todas las requests de un usuario salgan del mismo IP. ProxyHat lo soporta mediante el flag session- en el username:
# Cada "usuario" virtual mantiene el mismo IP durante su sesión
session_id = 'user-001-abc123'
proxy_url = f'http://user-country-US-session-{session_id}:pass@gate.proxyhat.com:8080'
La sesión persiste mientras el IP esté activo (típicamente varios minutos a horas según el pool). Si necesitas rotar, simplemente cambia el session_id.
Reintentos con backoff exponencial
Los bloqueos temporales (HTTP 429, 503) son inevitables en scraping a escala. Implementa reintentos con backoff:
import time, random
def get_with_retry(page, url, max_retries=3):
for attempt in range(max_retries):
try:
resp = page.get(url, retry=1, timeout=15)
if hasattr(resp, 'status') and resp.status == 200:
return resp
except Exception as e:
print(f'Intento {attempt+1} fallido: {e}')
wait = (2 ** attempt) + random.uniform(0, 1)
time.sleep(wait)
return None
Descubrir APIs ocultas con listen
Antes de construir selectores frágiles, usa listen para identificar si el sitio carga datos vía XHR. Si encuentras un endpoint JSON, puedes切换 a SessionPage y consumirlo directamente en HTTP, multiplicando el rendimiento:
# Fase de descubrimiento en Chromium
page = ChromiumPage()
page.listen.start('api/') # captura cualquier URL que contenga 'api/'
page.get('https://target-site.com/dashboard')
for _ in range(5):
packet = page.listen.wait(timeout=10)
if packet:
print(f'API encontrada: {packet.url}')
print(f'Body: {packet.response.body[:200]}')
Una vez identificada la API, puedes consumirla con SessionPage y el mismo proxy, evitando el navegador por completo.
Concurrencia y límites
DrissionPage no tiene un sistema de concurrencia integrado, pero funciona bien con concurrent.futures para SessionPage y con pools de instancias ChromiumPage para el navegador. Recomendamos:
- SessionPage: hasta 100 sesiones concurrentes por proceso, cada una con su propio proxy y sesión pegajosa.
- ChromiumPage: máximo 5–10 instancias simultáneas por máquina (cada instancia consume ~200–400 MB de RAM).
- Rate limiting: respeta 1–2 segundos entre requests al mismo dominio; usa
time.sleep()o un token bucket.
Para escalar más allá de una máquina, empaqueta cada worker en un contenedor Docker con su propia instancia de Chromium y proxy asignado. Consulta la documentación de ProxyHat para límites de concurrencia por plan.
Cuándo NO escalar al navegador y consideraciones éticas
El navegador es caro y lento. Escala a ChromiumPage solo cuando:
- El contenido crítico se carga vía JavaScript después de la navegación inicial.
- El sitio requiere interacción (clics, scrolls, formularios) para revelar datos.
- El sitio usa fingerprinting del navegador que un cliente HTTP no puede replicar.
No escaltes si:
- El HTML estático contiene los datos que necesitas.
- Existe una API XHR detectable con
listenque devuelve JSON. - El sitio ofrece una API oficial (úsala en su lugar).
Desde el punto de vista ético, el scraping de datos públicos es generalmente legal, pero hay matices importantes. La normativa RGPD de la UE protege datos personales: extraer nombres, emails o datos identificativos sin consentimiento puede violar la ley. En EE. UU., la Computer Fraud and Abuse Act (CFAA) ha sido interpretada de forma restrictiva tras el caso Van Buren v. United States (2021), pero acceder a sistemas con autenticación sin autorización sigue siendo riesgoso.
Buenas prácticas:
- Revisa
robots.txtantes de scrapear un dominio nuevo. - Identifica tu bot con un user-agent descriptivo cuando sea apropiado.
- Limita la tasa de requests para no degradar el servicio del objetivo.
- No extraigas datos personales sin base legal.
Conclusiones clave
Key Takeaways:
- DrissionPage unifica HTTP y navegador en un solo framework, compartiendo cookies y estado entre modos.
- Usa
SessionPagepara el 80% de requests (HTML estático, APIs JSON) y escala aChromiumPagesolo cuando el JS lo exija.- Configura proxies con
set.proxies()en SessionPage yChromiumOptions.set_proxy()en ChromiumPage.- Los proxies residenciales de ProxyHat con sesión pegajosa (
user-country-US-session-abc123) reducen bloqueos en sitios con anti-bot.- Usa
listen.start()para descubrir APIs ocultas y evitar el navegador cuando sea posible.- Limita la concurrencia: ~100 sesiones HTTP o 5–10 instancias Chromium por máquina.
- Respeta robots.txt, términos de servicio y normativa de protección de datos.
¿Listo para empezar? Revisa los casos de uso de web scraping y seguimiento de SERP con ProxyHat, o consulta nuestra página de precios para elegir el plan de proxies que mejor se adapte a tu volumen.






