Volver al Centro de conocimiento
Primeros pasos

Scraping con Puppeteer: opciones y valores observables

Relaciona las opciones de lanzamiento, viewport, almacenamiento y modo headless de Puppeteer con los valores de una página propia.

BotBrowser Team

Scraping con Puppeteer: opciones y valores observables
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 documenta un lanzamiento con perfil mediante puppeteer-core, executablePath, defaultViewport y --bot-profile; no garantiza una respuesta de terceros ni un resultado de detección.

Resumen

Las opciones de scraping con Puppeteer tienen ámbitos distintos. executablePath, headless y userDataDir afectan al proceso; defaultViewport afecta a la emulación de la página; BrowserContext controla el almacenamiento de sesión dentro del proceso. Una página puede leer dimensiones o navigator.webdriver, pero esos valores no son un veredicto, una prueba de identidad ni una autorización.

Utiliza un catálogo propio, fija el binario y el perfil, cambia una opción y guarda URL final, dimensiones, navigator.webdriver, número de registros aceptados y diagnósticos. La comparación permite refutar una hipótesis sin afirmar que un modo recibe mayor aceptación o protege más la privacidad.

Contenido

Opciones del proceso

La integración de lanzamiento con Puppeteer cubre la instalación del paquete y la conexión inicial con perfil; la comparación siguiente supone que esa integración ya está configurada.

executablePath selecciona el ejecutable. Que exista la ruta no prueba que se cargó la compilación esperada, que se aceptó el perfil ni que responderá un destino. Guarda ruta, versión del paquete, versión del ejecutable y argumentos para identificar cambios en las entradas del proceso.

headless tiene tres formas documentadas: true usa el modo headless actual de Chrome, 'shell' usa el binario independiente chrome-headless-shell y false abre el navegador con interfaz. Pueden diferir en comportamiento del proceso, renderizado y funciones de interfaz. Son modos de ejecución, no categorías de confianza, aceptación o acceso. Un catálogo informa de la salida de una página, no de la decisión de un servidor.

userDataDir es el directorio de datos persistentes del proceso. No es el perfil .enc de BotBrowser ni un BrowserContext. Los procesos concurrentes necesitan directorios de escritura distintos. Reutilizarlos puede conservar cookies y almacenamiento local, pero también compartir estado entre ejecuciones; documenta esa reutilización.

La configuración de Puppeteer en BotBrowser documenta puppeteer-core, el modo Chrome, executablePath, defaultViewport: null y --bot-profile. La referencia de perfiles describe sus valores. Ninguno de esos documentos promete una decisión de terceros ni una respuesta del destino.

Ámbitos de Puppeteer desde el proceso hasta la página y el registro de diferencias

Ámbito de página y contexto

Puppeteer aplica un viewport predeterminado de 800 por 600 mediante las opciones de página. defaultViewport: null elimina esa emulación predeterminada, pero no demuestra el tamaño de la pantalla del sistema, el estado del gestor de ventanas ni las dimensiones de un monitor físico. Registra window.innerWidth, window.innerHeight, screen.width y screen.height.

Un BrowserContext separa cookies y almacenamiento de otros contextos del mismo proceso. No elige otro ejecutable, no crea una IP pública, no concede permiso de recolección ni establece una identidad. El identificador del contexto y la política de almacenamiento son entradas del proceso, no pruebas de acceso.

El booleano de solo lectura navigator.webdriver es un valor visible para la página. MDN describe las condiciones en que Chrome controlado por automatización lo expone. Registrar true o false ayuda a explicar diferencias. Un detector puede utilizar más señales y un servidor recibe datos que la página desconoce. El análisis de los límites de los parches de Puppeteer y Playwright separa un cambio en JavaScript de los comportamientos que un parche no controla.

Separa instalación y ejecución. Los archivos de configuración de Puppeteer controlan descargas y caché; puppeteer-core los ignora. Cambiar la caché puede cambiar el binario instalado, pero no es una observación del viewport ni del almacenamiento. Registra el ejecutable resuelto y las versiones de los paquetes.

Campos del registro

Crea el registro antes del lanzamiento. Incluye identificador, versión del paquete, ejecutable resuelto, cadena de versión, nombre del argumento de perfil sin contenido secreto, valor headless, viewport, revisión del catálogo y origen propio. Estos campos permiten determinar si dos JSON son comparables. Excluye cookies, tokens, bytes del perfil y consultas con autenticación.

Opciones y observaciones

EntradaÁmbitoObservaciones y diagnósticos que registrarLo que no demuestra
executablePathProcesoVersión y API disponiblesQue se cargó el binario solicitado o que el servidor lo acepta
headlessProcesoViewport, renderizado, propiedades seleccionadasConfianza, autorización o veredicto
defaultViewportEmulación de páginainnerWidth, innerHeight, valores de pantallaDimensiones del monitor físico
userDataDirEstado persistenteCookies y almacenamiento disponiblesPerfil, IP pública o propiedad de una cuenta
BrowserContextAlmacenamiento de sesiónCookies locales y aislamientoOtro binario, identidad de red o permiso
navigator.webdriverCampo de páginaBooleano de solo lectura expuestoHuella completa o decisión del servidor

Verifica el origen: HTTP o HTTPS, propiedad del proyecto, terminado en /, sin credenciales, consulta ni fragmento. La ruta final debe permanecer en ese origen. Cada fila debe tener exactamente un nombre y un precio con el formato decimal acordado. Rechaza duplicados, enlaces de otro origen, moneda ausente y rutas inesperadas.

Reproducibilidad y cierre

Utiliza un RUN_USER_DATA_DIR nuevo por comparación independiente, salvo cuando la persistencia sea la variable. Mantén fijo el perfil y copia solo datos permitidos del catálogo. Limita el tiempo de navegación y distingue un timeout anterior de uno posterior a la respuesta. Un timeout describe transporte o disponibilidad, no demuestra un rechazo del navegador.

Manifiesto de entradas

Guarda junto al registro la versión de Puppeteer, resumen del archivo de bloqueo, versión del ejecutable, modo, viewport, política de contexto, revisión del catálogo e instante de inicio. Una ruta no basta: el ejecutable puede sustituirse y el directorio conservar estado anterior. El manifiesto fija las entradas sin copiar perfiles ni cuentas.

Conserva los tipos: headless es true, false o 'shell'; defaultViewport es null o un objeto con dimensiones numéricas. Representa userDataDir mediante un identificador redactado, no una ruta privada. Registra nombre y valor resuelto de las variables de entorno. No equipares una opción omitida con un valor explícito antes de conocer la versión de Puppeteer.

Registra URL solicitada, URL final, redirecciones, estado HTTP y tiempo hasta domcontentloaded. Responden a preguntas diferentes: una página del mismo origen puede ser un error y un 200 puede incumplir el esquema. Distingue un timeout anterior a la primera respuesta de uno ocurrido durante la carga de scripts. No combines fallos de transporte, navegación y datos en una sola etiqueta.

El catálogo necesita una señal determinista de disponibilidad. Dos filas fijas son más comparables que una lista que sigue creciendo. Espera al marcador de carga y verifica después el número de filas. Una red inactiva no prueba que la aplicación terminó: analítica, sondeos y Service Workers pueden continuar, y una página de error puede quedar inactiva inmediatamente.

Validación de datos

Valida antes de escribir: un nombre, un precio y una moneda de tres letras por fila. Si necesitas texto original para diagnóstico, consérvalo en un archivo local protegido; el registro compartido puede contener un resumen redactado y el resultado de validación. Dos filas aceptadas y cero filas son resultados distintos, sin implicar aceptación por otras rutas o servicios.

Identifica campo y regla fallidos: price-format, currency-code, duplicate-item o missing-name aportan más que schema=false. Añade revisión de selectores y resumen del catálogo: un cambio de marcado puede causar un fallo aunque las entradas del navegador sean idénticas.

Procedimiento de diferencias

Con ambos registros disponibles, ordena identificadores por su clave, normaliza barras finales de URL y compara dimensiones numéricas como números. Separa los campos no observados. Este formato es hipotético; no representa una ejecución real:

{
  "changedOption": "defaultViewport",
  "before": { "innerWidth": 800, "innerHeight": 600 },
  "after": { "innerWidth": 1280, "innerHeight": 720 },
  "same": ["origin", "itemCount", "currency"],
  "changed": ["innerWidth", "innerHeight"],
  "notObserved": ["screenX"],
  "interpretation": "The fixture read a different emulated viewport."
}

La interpretación debe nombrar observación y alcance, sin conclusiones sobre personas, detectores, propietarios del perfil o políticas del destino. Si difieren las versiones del ejecutable o del catálogo, marca not-comparable antes de interpretar campos. Dos entradas modificadas constituyen otro experimento.

Comparaciones de persistencia

Para estudiar persistencia, cambia únicamente esa variable. Ejecuta A con directorio nuevo, cierra el navegador y ejecuta B reutilizando el directorio. Registra las claves que comunica el catálogo propio, nunca valores de cookies ni tokens. Una tercera ejecución con directorio nuevo puede comprobar la dependencia de persistencia, pero no la propiedad de una cuenta ni la autorización.

El aislamiento de contextos requiere otra comparación: crea dos contextos en un proceso, carga el mismo origen y registra si cada uno ve el marcador. Ciérralos y repite con otro proceso si interesa la reutilización. El resultado describe almacenamiento del catálogo, no cambios de binario, ruta de red o decisión del servidor.

Versiones y reversión

Fija paquete y ejecutable durante la comparación. Si cambia una versión, inicia un par de registros nuevo aunque las opciones coincidan. Una actualización puede cambiar renderizado, valores predeterminados o campos expuestos. Conserva la diferencia normalizada previa y el motivo de adoptar la versión nueva.

Para revertir, restaura bloqueo de paquetes, ejecutable, referencia de perfil, argumentos, viewport, política de contexto y catálogo. Restaurar solo headless o defaultViewport no reproduce una ejecución con otro binario o almacenamiento. Versiona catálogo y bloqueo; guarda las referencias de perfil separadas de sus secretos.

Ejemplo acotado de una variable

La página propia expone dos filas data-testid="item" en /catalog, cada una con data-id único, nombre, precio y moneda. El catálogo establece data-ready="true" en data-testid="catalog" solo cuando ambas están completas. El script es ilustrativo, no un registro ejecutado; escribe únicamente tras validar respuesta, URL final, disponibilidad y esquema.

import puppeteer from 'puppeteer-core';
import fs from 'node:fs/promises';

const origin = new URL(process.env.OWNED_CATALOG_ORIGIN ?? '');
const executablePath = process.env.BOTBROWSER_EXEC_PATH;
const profilePath = process.env.BOT_PROFILE_PATH;
const mode = process.env.HEADLESS_MODE ?? 'true';
const viewportMode = process.env.DEFAULT_VIEWPORT ?? '800x600';
if (
  !['http:', 'https:'].includes(origin.protocol) ||
  origin.username ||
  origin.password ||
  origin.pathname !== '/' ||
  origin.search ||
  origin.hash
)
  throw new Error('OWNED_CATALOG_ORIGIN must be a plain HTTP(S) origin');
if (!['true', 'false'].includes(mode)) throw new Error('HEADLESS_MODE must be true or false');
if (!['null', '800x600'].includes(viewportMode)) throw new Error('invalid DEFAULT_VIEWPORT');
if (!executablePath || !profilePath || !process.env.RUN_USER_DATA_DIR)
  throw new Error('browser inputs and a dedicated RUN_USER_DATA_DIR are required');
const viewport = viewportMode === 'null' ? null : { width: 800, height: 600 };
const headless = mode === 'true';
const browser = await puppeteer.launch({
  browser: 'chrome',
  executablePath,
  headless,
  defaultViewport: viewport,
  args: [`--bot-profile=${profilePath}`],
  userDataDir: process.env.RUN_USER_DATA_DIR,
});
const diagnostics = [];
try {
  const context = await browser.createBrowserContext();
  const page = await context.newPage();
  const assertRoute = () => {
    const url = new URL(page.url());
    if (url.origin !== origin.origin || url.pathname !== '/catalog' || url.search || url.hash)
      throw new Error('unexpected catalogue URL');
  };
  const started = Date.now();
  const response = await page.goto(new URL('catalog', origin).href, {
    waitUntil: 'domcontentloaded',
    timeout: 15000,
  });
  assertRoute();
  if (!response || !response.ok()) throw new Error(`HTTP response rejected: ${response?.status()}`);
  const navigationMs = Date.now() - started;
  await page.waitForSelector('[data-testid="catalog"][data-ready="true"]', { timeout: 5000 });
  const items = await page.$$eval('[data-testid="item"]', nodes =>
    nodes.map(node => {
      if (
        node.querySelectorAll('[data-testid="item-name"]').length !== 1 ||
        node.querySelectorAll('[data-testid="item-price"]').length !== 1
      )
        throw new Error('name or price cardinality rejected');
      return {
        id: node.getAttribute('data-id'),
        name: node.querySelector('[data-testid="item-name"]')?.textContent?.trim(),
        price: node.querySelector('[data-testid="item-price"]')?.textContent?.trim(),
        currency: node.querySelector('[data-testid="item-price"]')?.getAttribute('data-currency'),
      };
    })
  );
  if (
    items.length !== 2 ||
    new Set(items.map(item => item.id)).size !== 2 ||
    items.some(
      item =>
        !item.id || !item.name || !/^\d+\.\d{2}$/.test(item.price ?? '') || !/^[A-Z]{3}$/.test(item.currency ?? '')
    )
  )
    throw new Error('fixture row schema rejected');
  const observed = await page.evaluate(() => ({
    innerWidth: window.innerWidth,
    innerHeight: window.innerHeight,
    screenWidth: screen.width,
    screenHeight: screen.height,
    webdriver: navigator.webdriver,
  }));
  assertRoute();
  diagnostics.push({
    finalUrl: page.url(),
    status: response.status(),
    navigationMs,
    redirectCount: response.request().redirectChain().length,
    itemCount: items.length,
  });
  await fs.writeFile(
    'puppeteer-scrape-record.json',
    JSON.stringify(
      {
        inputs: { headless, defaultViewport: viewport },
        controls: { userDataPolicy: 'fresh-empty-per-run', contextPolicy: 'new-isolated-context' },
        observed,
        items,
        diagnostics,
      },
      null,
      2
    ),
    { flag: 'wx' }
  );
} finally {
  await browser.close();
}

Mantén origen, ejecutable, perfil, catálogo, bloqueo y selectores iguales entre A y B. Cambia solo la opción estudiada, HEADLESS_MODE o DEFAULT_VIEWPORT, nunca ambas. Cada proceso usa otro RUN_USER_DATA_DIR, inicialmente vacío. Las rutas difieren, pero la política de control es fresh-empty-per-run; no todas las entradas brutas son idénticas. Verifica esa política antes del lanzamiento. El ejemplo compara headless o viewport, no persistencia.

El ejemplo acepta únicamente true y false en HEADLESS_MODE; headless-shell también cambiaría el binario. Ejecuta desde directorios de salida diferentes: flag: 'wx' impide sobrescribir un registro. Los objetos inputs contienen los valores estudiados; verifica que solo uno difiere y coinciden ambas políticas de control antes de asignar changedOption.

El script solo escribe un registro de éxito. Conserva aparte el manifiesto de cada invocación: no deduce los identificadores del paquete, ejecutable, perfil, catálogo ni selectores. En los fallos guarda por separado código de salida y error, y clasifica la etapa según las categorías siguientes. Ni un JSON ausente ni uno de una ejecución anterior prueba que la actual funcionó. Los errores de lanzamiento, navegación, disponibilidad o esquema siguen siendo invocaciones fallidas.

El contexto nuevo comienza con almacenamiento aislado: no hereda cookies del contexto predeterminado por apuntar userDataDir a un directorio persistente. Para comparar persistencia, utiliza siempre el contexto predeterminado o establece expresamente el mismo estado en cada contexto nuevo. La referencia de contextos, páginas y cierre explica la propiedad; cerrar el navegador también cierra páginas y contextos, incluso si falla la creación o validación de la página.

Alcance y límites de las observaciones

Cada campo tiene ámbito: viewport corresponde a emulación; userDataDir, a persistencia; headless, al modo del proceso; navigator.webdriver, a un booleano. Ninguno es una huella completa ni identifica por sí solo persona, dispositivo, propietario o veredicto.

Distingue same, changed y not-observed. Un campo ausente no equivale a false: puede faltar la API, el permiso o el dato del catálogo. Separa HTTP y datos de negocio aceptados. Un 200 puede contener un catálogo vacío o inválido y un registro válido no asegura otra respuesta.

Antes de interpretar, compara ejecutable resuelto, versión de Puppeteer, argumentos, directorio, creación del contexto, URL, estado, revisión del catálogo y resumen de selectores. Así un binario diferente, cookies antiguas o marcado modificado no se atribuyen al viewport.

Clasificación de fallos

executablePath inválido indica fallo de inicio; perfil ausente, fallo de entrada; userDataDir compartido, aislamiento incorrecto; URL final fuera del origen, fallo de autorización o navegación; 200 con cero filas aceptadas, fallo de esquema. Conserva categorías en vez de repetir el mismo comando para todo.

Si puppeteer-core ignora una configuración, revisa argumentos explícitos y binario resuelto. Si defaultViewport: null da dimensiones inesperadas, guarda las cuatro dimensiones y el entorno de pantalla, sin inferir el monitor físico. Si cambia navigator.webdriver, registra modo y argumentos, sin modificarlo.

Preguntas frecuentes

¿Qué cambia el modo headless?

Selecciona el modo de ejecución. La página observa algunas consecuencias y el servidor puede emplear más señales. El registro describe diferencias, sin prometer aceptación ni un veredicto.

¿userDataDir es un archivo de perfil?

No: es un directorio de datos persistentes. La entrada de perfil y el directorio tienen ciclos de vida y propiedad distintos; registra ambos.

¿Un BrowserContext nuevo crea otra identidad?

No. Aísla datos de sesión dentro del navegador. No concede autorización, cambia la IP pública ni establece la propiedad de la sesión.

¿Por qué registrar navigator.webdriver?

Es un campo concreto visible que ayuda a explicar diferencias. No es un veredicto y no debe borrarse ni reescribirse para este diagnóstico.

Fuentes

Conclusión práctica

Cambia una opción de lanzamiento o página manteniendo catálogo propio, ejecutable, perfil, contexto, versiones y selectores constantes. Guarda observaciones y diagnósticos exactos, clasifica fallos por ámbito y conserva lo desconocido como desconocido. La diferencia explica lo que leyó la página, no invisibilidad frente a detección, identidad del perfil ni aceptación obligatoria del destino.

Lecturas: contextos, páginas y cierre de Puppeteer y límites de los parches de Puppeteer y Playwright.

#Puppeteer#scraping web#Browser Automation#señales del navegador

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.