Volver al Centro de conocimiento
Primeros pasos

Scraping con navegador headless: qué cambia

Compara Chrome visible, headless unificado y headless shell para scraping. Registra renderer, viewport y webdriver sin afirmar el resultado de un sitio.

BotBrowser Team

Documentación

Quieres la documentación estructurada de Primeros pasos?

Este artículo forma parte de la biblioteca editorial. Para pasos de configuración, material de referencia y actualizaciones continuas, entra en la sección de docs.

BotBrowser permite usar Chrome con perfil para scraping visible o headless, pero el perfil no sustituye el renderer de Chrome ni garantiza la respuesta de un sitio remoto. Un script de extracción puede devolver contenido distinto en una ejecución con Chrome visible y otra headless, aunque ambas visiten la misma URL. La primera pregunta es qué implementación del navegador y qué modo de lanzamiento se ejecutaron; la segunda es si el viewport, la escala de dispositivo, las fuentes, el almacenamiento o el estado de la fixture cambiaron al mismo tiempo. Son causas distintas, no etiquetas intercambiables para un navegador visible o invisible.

Para comparar una ejecución de scraping de forma controlada, distingue tres casos: una ventana visible de Chrome, el modo headless unificado de Chrome y el headless shell opcional de Puppeteer. Chrome describe el headless unificado como el uso de la implementación normal de Chrome; el shell es un ejecutable separado. Ninguna descripción promete que la página produzca la misma salida ni predice si un servicio remoto aceptará una solicitud. Una fixture local permite ver qué valores cambiaron bajo una configuración declarada.

Chrome visible, headless unificado y headless shell usan la misma fixture local; compara las entradas de lanzamiento registradas con los campos de página, no con decisiones remotas

Resumen

  • headless: false abre una ventana visible; headless: true selecciona headless unificado en Puppeteer actual; headless: 'shell' selecciona la compilación shell separada de Puppeteer.
  • Mantén fijos la versión del navegador, la fixture, el viewport, la escala de dispositivo, el almacenamiento y los flags de lanzamiento antes de atribuir una diferencia al modo.
  • Registra window.innerWidth, screen.width, window.devicePixelRatio, navigator.webdriver y la versión del navegador como observaciones, no como una puntuación de identidad o aceptación.
  • Usa una fixture local propia y cambia una variable cada vez. Una diferencia visible para la página no demuestra que un detector de terceros haya aceptado o rechazado la ejecución.

Contenido

Tres modos de ejecución

«Headless» describe cómo se inicia un navegador, no un motor de renderizado universal. El binario, la versión, los flags de lanzamiento, el sistema operativo, el servidor de pantalla, el viewport, las fuentes y el perfil siguen siendo relevantes. Una comparación útil nombra el modo y el ejecutable en lugar de guardar solo un valor booleano.

Chrome con ventana visible

En una ejecución visible, Chrome crea una ventana de nivel superior que se muestra en pantalla. El gestor de ventanas y el entorno de visualización pueden afectar las dimensiones exteriores de la ventana, el tamaño de pantalla disponible, la escala, el foco y el estado de visibilidad. Un harness de pruebas puede establecer un viewport de página independiente de la ventana exterior, por lo que «visible» no significa que la página reciba las dimensiones del monitor físico.

En Puppeteer, headless: false solicita el modo visible. La página puede leer el viewport interior mediante window.innerWidth y window.innerHeight; screen.width y screen.height describen las dimensiones de pantalla expuestas al documento. Estos valores responden preguntas distintas. Un viewport fijo puede estabilizar el primer par mientras la pantalla o el gestor de ventanas cambia el segundo.

Chrome headless unificado

El modo headless actual de Chrome utiliza la implementación normal del navegador Chrome, en lugar de la antigua implementación headless separada. La documentación de Chrome describe este cambio a partir de Chrome 112. Es un límite de implementación: no significa que una sesión headless tenga una ventana visible de escritorio ni que todos los valores visibles para la página deban coincidir con una sesión visible en cualquier host.

Puppeteer utiliza headless: true para este modo unificado. Un lanzamiento por línea de comandos también puede solicitar ejecución headless. El ejecutable y la versión exactos siguen importando: un paquete de automatización antiguo, un binario fijado o un flag personalizado puede cambiar qué se ejecutó realmente. Registra browser.version() o la versión negociada equivalente junto al modo, en vez de asumir que el paquete instalado seleccionó el navegador esperado.

Headless shell

Puppeteer también ofrece headless: 'shell', que selecciona el ejecutable separado chrome-headless-shell. No es simplemente otro nombre para Chrome headless unificado. La documentación de Chrome describe el shell como un binario headless distinto, y la guía de Puppeteer documenta su selector de modo separado. Por tanto, una comparación shell frente a visible puede cambiar tanto el modo de lanzamiento como la implementación del ejecutable si el experimento no registra ambos.

La opción es específica de Puppeteer; otras bibliotecas de automatización pueden usar nombres distintos o no ofrecer el modo shell mediante la misma API. No conviertas una opción de cadena de Puppeteer en un ajuste de Playwright sin consultar la documentación actual de esa biblioteca. Para configuración del navegador, no para semántica de modos, la guía de integración con Puppeteer y las notas de despliegue headless cubren preguntas operativas distintas.

Etiqueta de ejecuciónSelector PuppeteerImplementación que se debe registrarLo que la etiqueta no demuestra
Ventana visibleheadless: falseEjecutable y versión de ChromeQue el resultado de la página coincida con un dispositivo real
Headless unificadoheadless: trueImplementación normal de Chrome y versiónQue la página no pueda distinguir el entorno
Headless shellheadless: 'shell'Ejecutable y versión de chrome-headless-shellQue shell y Chrome normal expongan un comportamiento idéntico

Chrome 132 puso la implementación headless anterior a disposición como un binario shell independiente. Por eso los números de versión pertenecen al registro: los nombres de modo pueden seguir pareciéndose mientras cambia el ejecutable que hay detrás de un ajuste de automatización. Al actualizar un runner, consulta la documentación de Chrome Headless y la guía de modos headless de Puppeteer; no infieras la implementación seleccionada a partir de un flag copiado de un artículo antiguo.

Qué puede leer una página

Una página no recibe una propiedad universal isHeadless estandarizada. Lee APIs del navegador, resultados de layout y comportamientos expuestos por el entorno configurado. Una fixture útil registra unos pocos campos concretos y conserva sus significados separados, en lugar de colapsarlos en una «huella headless».

window.innerWidth y window.innerHeight describen el viewport de layout en píxeles CSS. screen.width y screen.height describen las dimensiones de pantalla expuestas al documento. window.devicePixelRatio relaciona píxeles CSS con píxeles de dispositivo en la configuración de pantalla actual. Una página adaptable puede elegir otra composición cuando cambia el viewport aunque la implementación y la URL del navegador permanezcan iguales. Las referencias de MDN para innerWidth y devicePixelRatio especifican el significado de estas APIs; no afirman que el modo headless por sí solo determine sus valores.

navigator.userAgent y, cuando está disponible, navigator.userAgentData exponen datos de identificación del navegador sujetos a reglas de reducción y disponibilidad. Esas cadenas no son una forma fiable de etiquetar una sesión como visible o headless. El valor de User-Agent puede mantenerse mientras cambian el viewport o los resultados de renderizado, y el código de página no debería tratarlo como prueba de un dispositivo físico.

navigator.webdriver es una señal relacionada con la automatización estandarizada por WebDriver. MDN documenta cuándo el navegador informa de ese estado de automatización; Chrome puede exponerlo en configuraciones de automatización que incluyen ejecución headless. No es un clasificador completo de modos: un valor true significa que el navegador informa de control WebDriver, no que un sitio concreto haya llegado a un veredicto. La referencia de Navigator.webdriver en MDN es la fuente adecuada para el contrato de la propiedad. Una comparación separada de señales headless explica cómo registrar varias superficies visibles para el navegador sin tratar ningún campo aislado como resultado de detección.

Otros resultados dependen de más que el modo de lanzamiento. La disponibilidad de fuentes puede cambiar los saltos de línea y las dimensiones de elementos. El idioma y la zona horaria pueden cambiar el texto formateado. Una cookie almacenada puede cambiar si una página muestra una pantalla de inicio de sesión o una tabla de datos. La ruta de red puede cambiar la respuesta del servidor o los tiempos. Una ejecución de scraping que cambia varios de estos factores a la vez no puede atribuir la diferencia resultante del DOM al modo headless.

El entorno de despliegue es otro eje independiente. La configuración de servidor headless de BotBrowser documenta sus requisitos de pantalla virtual y perfil para ese producto. La referencia de consistencia de automatización describe valores del navegador basados en perfiles. Son detalles operativos de BotBrowser, no afirmaciones sobre la arquitectura de Chrome ascendente ni una promesa de que una página reciba valores idénticos en todos los modos.

Una comparación controlada de dos modos

Usa una página propia o que tengas autorización para inspeccionar. Una fixture local evita mezclar observaciones del modo de lanzamiento con autorización, límites de solicitudes, desafíos, cambios de contenido, políticas del servicio o estado de una web remota. El ejemplo siguiente espera una fixture en http://127.0.0.1:4173/fixture.html; no se ha ejecutado aquí y no contiene resultados medidos.

Ejecuta un modo por proceso. Usa la misma instalación de Puppeteer, ejecutable, versión del navegador, página local, política de perfil, viewport, escala de dispositivo, idioma y estado de almacenamiento. El nombre del modo es la única variable intencional para las dos primeras ejecuciones. La ejecución shell es una comparación separada porque el ejecutable es distinto.

import puppeteer from 'puppeteer';

const modeName = process.argv[2];
const modes = {
  headed: false,
  headless: true,
  shell: 'shell',
};

if (!Object.hasOwn(modes, modeName)) {
  throw new Error('Choose one mode: headed, headless, or shell');
}

const fixture = 'http://127.0.0.1:4173/fixture.html';
const browser = await puppeteer.launch({
  headless: modes[modeName],
  defaultViewport: {
    width: 1365,
    height: 768,
    deviceScaleFactor: 1,
  },
});

try {
  const page = await browser.newPage();
  await page.goto(fixture, { waitUntil: 'domcontentloaded', timeout: 10000 });
  const pageFields = await page.evaluate(() => ({
    origin: location.origin,
    userAgent: navigator.userAgent,
    webdriver: navigator.webdriver,
    viewport: { width: innerWidth, height: innerHeight },
    screen: { width: screen.width, height: screen.height },
    devicePixelRatio,
    visibilityState: document.visibilityState,
  }));

  process.stdout.write(
    `${JSON.stringify(
      {
        mode: modeName,
        browserVersion: await browser.version(),
        pageFields,
      },
      null,
      2
    )}\n`
  );
} finally {
  await browser.close();
}

Guarda el mismo script como mode-diff.mjs y captura cada ejecución por separado:

node mode-diff.mjs headed > headed.json
node mode-diff.mjs headless > headless.json
node mode-diff.mjs shell > shell.json
diff -u headed.json headless.json

El código imprime un registro, no un resultado de aprobado o rechazado. Intencionalmente no contacta con un dominio de terceros, no compara una cuenta ni afirma que algún modo sea aceptado. Una salida distinta de cero o un archivo JSON ausente indica un problema de ejecución; los valores diferentes son observaciones que hay que clasificar. Para una comparación estricta entre visible y headless unificado, compara los dos primeros archivos y verifica que browserVersion coincide. Trata la salida shell como una comparación de implementación separada y conserva su versión en las notas.

Los valores de viewport del ejemplo son entradas fijas, no evidencia de un viewport universal. defaultViewport establece el viewport de página de Puppeteer; no controla todas las dimensiones de ventana del sistema operativo. El script registra las dimensiones de pantalla y la escala de dispositivo porque pueden revelar un cambio del host. Si una fixture necesita un viewport específico para reproducir una rama adaptable, conserva ese valor en cada ejecución y anótalo junto a los archivos JSON.

En una fixture local con datos dinámicos, haz que la página de prueba sea determinista antes de comparar modos. Fija la versión de la fixture, usa recursos locales estáticos, congela el estado de la aplicación y evita una API en vivo cuya respuesta pueda cambiar entre capturas. Si una fuente se carga desde la red, una diferencia de tiempo puede cambiar el layout aunque el renderer sea el mismo. Conserva la revisión de la fuente de la página de prueba y el conjunto de recursos junto al registro de captura para que una actualización futura del navegador no se compare con otra fixture.

Clasificación de diferencias

Empieza por la versión del navegador y la configuración de lanzamiento registradas. Si no coinciden con la comparación prevista, no es una comparación controlada de modos. Después comprueba si el ancho CSS de la fixture cruza un breakpoint. Un innerWidth distinto puede seleccionar otro breakpoint CSS; un devicePixelRatio distinto puede cambiar la rasterización y la resolución de respaldo del canvas. Estos resultados explican una diferencia de página mediante entradas concretas, no mediante una etiqueta headless genérica.

Separa tres clases de resultado:

ClaseEjemploSiguiente diagnóstico
Diferencia de entrada declaradaLa ejecución shell informa otra versión o ejecutableConsérvala como comparación de binarios; no la atribuyas solo a la visibilidad
Diferencia mediada por el entornoCambian dimensiones de pantalla, fuentes, idioma o almacenamientoAlinea la entrada o registra la diferencia como parte del entorno
Diferencia observada por la páginaUna fixture local lee otro valor de navigator.webdriver o selecciona otro layoutVerifica el contrato de la API y la configuración del navegador; no infieras una decisión de un detector remoto

Por ejemplo, un cambio en navigator.webdriver debe rastrearse hasta el control de automatización y la configuración de lanzamiento del navegador. La propiedad informa sobre el estado relacionado con WebDriver; no indica si el scraping está autorizado ni si un sitio aceptó la sesión. Un cambio de innerWidth debe comprobarse frente al viewport de página configurado antes de investigar CSS. Un cambio de screen.width señala una configuración de pantalla expuesta a la página y no debería renombrarse como identidad de un dispositivo físico.

La comparación de modos tampoco puede aislar el comportamiento del servidor. Dos ejecuciones pueden recibir HTML distinto porque cambiaron la fixture, las cookies, el tiempo de solicitud o el contenido cacheado. Compara la respuesta de la fixture y el estado de su fuente antes de atribuir una diferencia del DOM a Chrome. Si se trata de una web remota, sigue su política de acceso documentada y separa las respuestas del servidor de las lecturas de APIs del navegador; este texto no proporciona instrucciones para eludir desafíos o controles de acceso.

Si el propósito es entender cómo un detector concreto agrupa observaciones, esa es una pregunta distinta de elegir un modo de lanzamiento para scraping. La página de capas de detección headless de CreepJS cubre categorías públicas de señales, mientras que la consistencia de perfiles examina valores basados en perfiles. Ninguna convierte una comparación local en una predicción sobre un servicio que no se midió.

Cuándo la comparación no es concluyente

La comparación no es concluyente cuando se desconoce el ejecutable real, difieren las versiones, cambió la fixture o las ejecuciones no usaron el mismo viewport y estado. También es no concluyente cuando solo se conserva una captura de pantalla sin metadatos de lanzamiento: una imagen muestra píxeles, pero no qué binario, flags, perfil, fuentes, idioma o configuración de pantalla la produjo.

Una observación aislada no demuestra que un comportamiento pertenezca a todos los navegadores headless. Las versiones cambian. Puppeteer y Chrome pueden actualizarse con calendarios distintos. El ajuste headless: true solo es significativo en la versión de biblioteca que lo interpreta y para el ejecutable que lanza. Fija esas dependencias para hacer una comparación reproducible y vuelve a ejecutar la fixture propia tras una actualización deliberada.

No combines cambios de modo, viewport, perfil, idioma y red en una sola ejecución «antes y después». Cuando se mueven varias variables, el resultado de la página tiene varias causas plausibles. Construye una matriz pequeña: visible y headless unificado con el mismo ejecutable, versión y viewport; después una fila shell separada con su ejecutable real; y, solo si hace falta, una segunda prueba de viewport manteniendo fijo el modo. Cada par responde a una pregunta acotada.

El comportamiento documentado de perfiles de BotBrowser pertenece a esa matriz como configuración de producto, no como modo upstream de Chrome. Un perfil puede suministrar valores documentados del navegador y la página de despliegue de servidor describe restricciones operativas. Ninguna afirmación significa que cambie la arquitectura del renderer, que todas las señales del host sean idénticas ni que un sitio devuelva el mismo contenido. La frontera entre consistencia basada en perfiles y salida visible/headless mantiene separadas esas preguntas.

Preguntas frecuentes

¿Chrome headless siempre se puede detectar?

No hay un booleano universal admitido que responda a eso para cada página y versión del navegador. Una página puede leer APIs y resultados de layout, y la configuración de automatización puede exponer estado relacionado con WebDriver. Una lectura concreta es evidencia de esa ejecución, no una garantía de que todos los sitios utilicen los mismos campos o lleguen a la misma conclusión. El resultado defendible es un registro de versión del navegador, modo de lanzamiento, entradas, fixture y campos observados.

¿Chrome headless genera la misma página que Chrome visible?

La implementación headless unificada de Chrome comparte la implementación normal de Chrome, pero compartir implementación no iguala todas las entradas del entorno. Una ventana visible de escritorio, una pantalla virtual, las fuentes, la pila gráfica, el viewport, la escala del dispositivo y el perfil pueden diferir. Compara el mismo ejecutable y una fixture controlada antes de atribuir un cambio al modo de lanzamiento. No prometas píxeles o HTML idénticos entre sistemas operativos.

¿headless: 'shell' es solo otra forma de escribir headless: true?

No. Puppeteer documenta el selector shell como un ejecutable headless shell separado, mientras que true selecciona Chrome headless unificado. Las versiones del paquete y las opciones de instalación determinan qué binario está disponible. Registra la versión del navegador de cada ejecución; no mezcles resultados shell en una base headless unificada sin documentar ese cambio de implementación.

¿Un scraper debería cambiar a modo visible cuando los datos difieren?

Primero identifica el campo que cambió y revisa las entradas controladas. Si la diferencia procede del viewport, las fuentes, el almacenamiento o el estado de la página, cambiar el modo puede ocultar la causa en vez de corregirla. Si un servicio remoto cambia su respuesta, sigue sus reglas de acceso documentadas e investiga la respuesta del servidor por separado. Cambiar el modo de lanzamiento no es un método para eludir controles de acceso ni demuestra que la extracción esté permitida o sea fiable.

Fuentes

Conclusión práctica

Elige un modo de lanzamiento para el despliegue y la carga de trabajo, y registra qué se ejecutó realmente. Compara visible y headless unificado sobre la misma fixture local, con una sola versión, viewport, política de perfil y estado; trata shell como otro ejecutable. El diff resultante puede explicar cambios visibles para la página en esa configuración. No certifica identidad, autorización ni la respuesta de un sitio.

#Headless Browser#scraping web#Chrome

Este artículo forma parte de la guía: Primeros pasos con automatización de navegador: mapa inicial

Ejecuta la misma comprobación en BotBrowser

Carga un perfil, mantén dentro del navegador las señales que acabas de leer y confirma el resultado en tu propia máquina antes del despliegue.