En este recorrido, cambiamos nuestro enfoque hacia la utilización de navegadores sin cabeza para raspar sitios web dinámicos. Un navegador sin cabeza es un navegador sin una interfaz gráfica de usuario que puede imitar las interacciones del usuario como hacer clic, desplazarse y completar formularios. Esta funcionalidad nos permite raspar sitios web que requieren interacción del usuario. ¿Cuáles son las herramientas disponibles para este propósito y cómo podemos emplearlas de manera efectiva? En el camino, encontraremos varios desafíos, así como descubriremos consejos y trucos valiosos para superarlos.
Una de las preguntas más comunes sobre el raspado de sitios web es: "¿Por qué mi raspador no puede ver los datos que veo en mi navegador web?

En el lado izquierdo está lo que vemos en el navegador, mientras que en el lado derecho está lo que nuestro raspador recupera. Entonces, ¿dónde va el resto de los datos?
Las páginas web dinámicas utilizan tecnologías web complejas impulsadas por JavaScript para descargar el procesamiento al lado del cliente. En otras palabras, proporcionan a los usuarios datos y lógica pero requieren que se ensamblen para visualizar la página completa. Por eso nuestro raspador solo ve una página en blanco.
Aquí hay un ejemplo simple:
<!DOCTYPE html>
<html>
<head>
<title>Ejemplo de página dinámica</title>
</head>
<body>
<div id="content">Cargando...</div>
<script>
var data = {
name: "John Doe",
age: 30,
city: "Nueva York",
};
document.addEventListener("DOMContentLoaded", function () {
var content = document.getElementById("content");
content.innerHTML = "<h1>" + data.name + "</h1>";
content.innerHTML += "<p>Edad: " + data.age + "</p>";
content.innerHTML += "<p>Ciudad: " + data.city + "</p>";
});
</script>
</body>
</html>
Cuando abrimos la página y desactivamos JavaScript, vemos "Cargando...". Pero cuando JavaScript está habilitado, vemos "John Doe", "Edad: 30" y "Ciudad: Nueva York". Esto se debe a que JavaScript se ejecuta después de que se carga la página, por lo que nuestro raspador solo ve "Cargando...". Por lo tanto, el raspador no ve la página completamente renderizada ya que carece de la capacidad de cargar la página usando JavaScript como lo hace un navegador web.
Podemos raspar sitios web dinámicos integrando un navegador real y manipulándolo. Hay varias herramientas disponibles para este propósito, como Puppeteer, Selenium y Playwright. Estas herramientas proporcionan APIs para controlar el navegador, lo que nos permite simular el comportamiento del usuario.
Los navegadores modernos como Chrome y Firefox vienen con protocolos de automatización integrados que permiten a otros programas controlarlos.
Actualmente, hay dos protocolos de automatización para navegadores:
Para ilustrar este desafío, tomemos un ejemplo de raspado de una página web real. Rastrearemos un sitio web en línea, como Experiencias de Airbnb. Describiremos brevemente la tarea de demostración y exploraremos cómo renderizar completamente una página de experiencia, por ejemplo, https://www.airbnb.com/experiences/1653933, y recuperar el contenido completamente renderizado para su procesamiento posterior.

Airbnb es uno de los sitios web generados dinámicamente construidos con React en todo el mundo. Esto significa que nuestro rastreador no podrá ver la página completamente renderizada. Sin un navegador sin cabeza, tendríamos que ingeniar a la inversa el código del sitio web para raspar su contenido HTML completo. Sin embargo, con la aparición de navegadores sin cabeza, podemos simular el comportamiento del usuario para raspar sitios web dinámicos, lo que hace que nuestro proceso sea mucho más simple.
Ahora intentemos usar cuatro herramientas diferentes de automatización de navegadores: Puppeteer, Selenium, Playwright y Nstbrowser.
Puppeteer es una biblioteca de Node.js desarrollada por el equipo de Google Chrome que proporciona una API de alto nivel para controlar Chrome o Chromium a través del protocolo DevTools. Es un navegador sin cabeza, lo que significa que no tiene una interfaz gráfica de usuario pero puede simular el comportamiento del usuario.
En comparación con Selenium, Puppeteer admite menos idiomas y navegadores, pero implementa completamente el protocolo CDP y se beneficia del sólido equipo de Google detrás de él.
Puppeteer también se describe a sí mismo como un cliente de automatización de navegadores genérico en lugar de adaptado al mercado de nicho para pruebas web, una buena noticia ya que los problemas de rastreo web reciben soporte oficial.
Veamos cómo se ve nuestro ejemplo de airbnb.com en Puppeteer y JavaScript:
const puppeteer = require('puppeteer');
(async () => {
const browser =
await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://airbnb.com/experiences/1653933');
await page.waitForSelector("h1");
await page.content();
await browser.close();
})();
Selenium fue uno de los primeros clientes de automatización a gran escala creados para automatizar pruebas de sitios web. Admite dos protocolos de control de navegador: WebDriver y CDP (solo desde Selenium V4+).
Al ser la herramienta más antigua en la lista de hoy, Selenium tiene una comunidad bastante grande y muchas características. Es compatible con casi todos los lenguajes de programación y puede ejecutarse en casi todos los navegadores web:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support.expected_conditions import visibility_of_element_located
chrome_options = Options()
chrome_options.add_argument("--headless")
browser = webdriver.Chrome(chrome_options) # iniciar un navegador web
browser.get("https://www.airbnb.com/experiences/1653933") # navegar a la URL
# esperar a que la página se cargue
# esperando a que aparezca el elemento <h1> en la página
title = (
WebDriverWait(driver=browser, timeout=10)
.until(visibility_of_element_located((By.CSS_SELECTOR, "h1")))
.text
)
# recuperar el contenido HTML completamente renderizado
content = browser.page_source
browser.close()
# luego podríamos analizarlo con beautifulsoup
from bs4 import BeautifulSoup
soup = BeautifulSoup(content, "html.parser")
print(soup.find("h1").text) # verás "Drink and Draw"

Arriba, primero abrimos una ventana del navegador web, luego navegamos a una página de experiencia de Airbnb. A continuación, esperamos a que la página se cargue esperando a que aparezca el primer elemento h1 en la página. Finalmente, extraemos el contenido HTML de la página y lo analizamos con BeautifulSoup.
Playwright es una biblioteca de automatización de navegadores síncrona y asíncrona proporcionada por Microsoft en varios idiomas.
El objetivo principal de Playwright es la prueba confiable de extremo a extremo de aplicaciones web modernas, aunque aún implementa todas las características comunes de automatización de navegadores (como Puppeteer y Selenium) y tiene una creciente comunidad de raspado web.
from playwright.sync_api import sync_playwright
with sync_playwright() as pw:
browser = pw.chromium.launch(headless=True)
context = browser.new_context(viewport={"width": 1920, "height": 1080})
page = context.new_page()
page.goto("https://airbnb.com/experiences/1653933") # ir a la url
page.wait_for_selector('h1')
title = page.inner_text('h1')
print(title) # verás "Drink and Draw"
En el código anterior, primero iniciamos un navegador Chromium, luego navegamos a una página de experiencia de Airbnb. A continuación, esperamos a que la página se cargue y luego extraemos el contenido HTML de la página.
Nstbrowser es un potente navegador anti-detección diseñado para profesionales que gestionan múltiples cuentas, lo que permite la gestión segura y eficiente de múltiples cuentas. Nstbrowser incorpora una variedad de tecnologías avanzadas, incluidos navegadores anti-detección, cuentas múltiples, navegadores virtuales, huellas dactilares de navegador y gestión de cuentas, proporcionando a los usuarios una nueva experiencia de navegación. A continuación, utilizaremos su navegador para recuperar el contenido de la página web en modo sin cabeza.

import json
from urllib.parse import quote
from urllib.parse import urlencode
import requests
from requests.exceptions import HTTPError
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support.expected_conditions import visibility_of_element_located
from selenium.webdriver.chrome.service import Service as ChromeService
# get_debugger_port: Obtener el puerto del depurador
def get_debugger_port(url: str):
try:
resp = requests.get(url).json()
if resp['data'] is None:
raise Exception(resp['msg'])
port = resp['data']['port']
return port
except HTTPError:
raise Exception(HTTPError.response)
def exec_selenium(debugger_address: str):
options = webdriver.ChromeOptions()
options.add_experimental_option("debuggerAddress", debugger_address)
# Reemplazar con la ruta correspondiente de WebDriver.
chrome_driver_path = r'/Users/xxx/Desktop/chrome_web_driver/chromedriver_mac_arm64/chromedriver'
service = ChromeService(chrome_driver_path)
driver = webdriver.Chrome(service=service, options=options)
driver.get("https://www.airbnb.com/experiences/1653933")
# esperar a que la página se cargue
# esperando a que aparezca el elemento <h1> en la página
title = (
WebDriverWait(driver=driver, timeout=10)
.until(visibility_of_element_located((By.CSS_SELECTOR, "h1")))
.text
)
content = driver.page_source
# luego podríamos analizarlo con beautifulsoup
from bs4 import BeautifulSoup
soup = BeautifulSoup(content, "html.parser")
print(soup.find("h1").text) # verás "Drink and Draw"
driver.close()
driver.quit()
def create_and_connect_to_browser():
host = '127.0.0.1'
api_key = 'Tu clave API aquí' # consíguela en https://app.nstbrowser.io/
config = {
'headless': True, # abrir navegador en modo sin cabeza
'remoteDebuggingPort': 9222,
'userAgent': 'Mozilla/5.0 (Macintosh; Intel Mac OS
X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
# userAgent compatible desde v0.15.0
'fingerprint': { # requerido
'name': 'navegador personalizado',
'platform': 'mac', # soportado: windows, mac, linux
'kernel': 'chromium', # solo soportado: chromium
'kernelMilestone': '113', # soportado: 113, 115, 118, 120
'hardwareConcurrency': 4, # soportado: 2, 4, 8, 10, 12, 14, 16
'deviceMemory': 4, # soportado: 2, 4, 8
'proxy': '', # formato de entrada: esquema://usuario:contraseña@host:puerto ej: http://usuario:contraseña@localhost:8080
}
}
query = urlencode({
'x-api-key': api_key, # requerido
'config': quote(json.dumps(config))
})
url = f'http://{host}:8848/devtool/launch?{query}'
print('URL de herramienta de desarrollo: ' + url)
port = get_debugger_port(url)
debugger_address = f'{host}:{port}'
print("Dirección del depurador: " + debugger_address)
exec_selenium(debugger_address)
create_and_connect_to_browser()
Después de ejecutar con éxito, veremos los siguientes resultados en la consola:

Con suerte, a través de este tutorial, has aprendido los siguientes dos puntos clave: