Ir al contenido

Cómo tomar capturas de pantalla con Puppeteer para un web scraping eficaz

Puppeteer es una biblioteca de Node.js que proporciona una API potente para controlar Chrome y Chromium sin cabeza a través del protocolo DevTools. Una de sus características más útiles es la capacidad de capturar capturas de pantalla de páginas y elementos web mediante programación.

Para los web scrapers, poder tomar capturas de pantalla con Puppeteer desbloquea una variedad de casos de uso valiosos:

  • Depuración visual de problemas de scraping y fallas de pruebas.
  • Captura de estados de páginas dinámicas y SPA.
  • Monitoreo de regresiones visuales y cambios en la interfaz de usuario.
  • Creación de tutoriales y documentación con capturas de pantalla para contexto.
  • Generación de activos de imágenes a partir de páginas web.

En esta guía completa, exploraremos cómo aprovechar las capturas de pantalla de Puppeteer para mejorar sus flujos de trabajo de web scraping.

El auge del titiritero para el web scraping

Puppeteer se lanzó por primera vez en 2017 y ha experimentado una rápida adopción por parte de la comunidad de web scraping. Aquí hay algunas estadísticas que resaltan su popularidad:

  • Más de 52,000 estrellas en Github lo convierten en uno de los mejores proyectos de JS.
  • Más de 3 millones de descargas semanales en NPM.
  • Crecimiento interanual del 490 % en las búsquedas de Titiritero en Google en 2022.

Entonces, ¿qué diferencia a Puppeteer del web scraping?

Control de navegador sin cabeza

Puppeteer proporciona control total sobre un navegador sin cabeza a través del protocolo Chrome DevTools. Esto permite replicar las interacciones del usuario para la automatización y extraer contenido dinámico.

Ligero y rápido

Ser solo sin cabeza significa que Puppeteer se salta toda la representación de la interfaz de usuario que hace que Chromium sea pesado. Esto da como resultado un rendimiento rápido para el raspado a escala.

Desarrollo activo

Con el respaldo del equipo de Chrome en Google, Puppeteer recibe actualizaciones frecuentes y nuevas funciones diseñadas para casos de uso de automatización y scraping.

Más simple que el selenio

Puppeteer solo se enfoca en controlar Chromium, mientras que Selenium admite múltiples navegadores. La API es mucho más limpia e idiomática, lo que la hace fácil de usar.

Por estas razones, muchos web scrapers están cambiando a Puppeteer desde Selenium/WebDriver para mejorar la velocidad, confiabilidad y capacidad.

Ahora profundicemos en cómo aprovechar las poderosas capacidades de captura de pantalla de Puppeteer.

Capturar capturas de pantalla de página completa

La forma más sencilla de tomar una captura de pantalla de una página completa es usar el page.screenshot() método:

// Launch browser
const browser = await puppeteer.launch();

// Open page 
const page = await browser.newPage();
await page.goto(‘https://example.com‘);

// Screenshot
await page.screenshot({
  path: ‘fullpage.png‘ 
});

Esto captura la ventana gráfica actualmente visible. Para hacer una captura de pantalla de la altura completa de la página, configure el fullPage opción de true:

await page.screenshot({
  path: ‘longpage.png‘,
  fullPage: true
}); 

Especificación de opciones de imagen

La screenshot() El método acepta opciones para controlar el tipo, la calidad y más:

  • type – png, jpeg o webp. El valor predeterminado es png.
  • quality – Para jpeg/webp, la calidad oscila entre 0 y 100. El valor predeterminado es 80.
  • omitBackground – Oculta el fondo blanco predeterminado y permite la transparencia.
  • encoding – Puede generar como base64 en lugar de guardar un archivo.

Por ejemplo, para guardar un archivo jpeg de alta calidad:

await page.screenshot({
  path: ‘page.jpeg‘,
  type: ‘jpeg‘,
  quality: 100
});

Consejo: Utilice webp para una mejor compresión con calidad equivalente. Sin embargo, webp puede tener problemas de compatibilidad.

Manejo de capturas de pantalla grandes

Las capturas de pantalla de página completa pueden superar fácilmente muchos megabytes de tamaño. De forma predeterminada, Puppeteer almacena capturas de pantalla en la memoria antes de guardarlas, lo que puede exceder los límites del proceso.

Para manejar capturas de pantalla grandes, pase la opción encoding: ‘base64‘ para obtener la cadena base64 en lugar de un búfer. Luego guarde usando fs.writeFile() para evitar almacenar la imagen en la memoria.

He aquí un ejemplo:

const buffer = await page.screenshot({ encoding: ‘base64‘ });

fs.writeFile(‘screenshot.png‘, buffer, ‘base64‘, err => {
  // handle error 
});

Desplazamiento de páginas altas para capturas de página completa

Para capturar la altura completa de páginas más largas que la ventana gráfica, primero tendremos que desplazarnos por la página.

Aquí hay un enfoque usando page.evaluate():

// Scroll to bottom  
await page.evaluate(() => {
  window.scrollTo(0, document.body.scrollHeight);
});

// Screenshot full scrollable area
await page.screenshot({ path: ‘longpage.png‘, fullPage: true });

También podemos desplazarnos de forma incremental tomando capturas de pantalla y luego unirlas en una sola captura de pantalla alta. Esto evita tener que almacenar toda la imagen en la memoria intermedia.

Alternativa: Guardar como PDF

Otra opción para capturar contenido de página completa: ¡generar un PDF!

// Generates PDF and saves to disk 
await page.pdf({
  path: ‘page.pdf‘,
  printBackground: true
});

Ventajas de los archivos PDF:

  • Maneja contenido de varias páginas desde el primer momento.
  • El formato vectorial suele dar como resultado archivos de menor tamaño.
  • El formato de impresión permanece intacto.

Contras:

  • Menos flexible para el procesamiento programático.
  • Opciones de estilo limitadas en comparación con las imágenes.
  • Es posible que no capture contenido renderizado dinámicamente.

Configuración del tamaño de la ventana gráfica

De forma predeterminada, Puppeteer utiliza una ventana gráfica de 800 px x 600 px. Para obtener capturas de pantalla precisas de página completa en diferentes tamaños de escritorio y dispositivos móviles, podemos configurar explícitamente la ventana gráfica:

// 1200px wide desktop 
await page.setViewport({
  width: 1200,
  height: 800  
});

// 400px wide mobile
await page.setViewport({
  width: 400,
  height: 1200 
});

Luego, las capturas de pantalla coincidirán con el tamaño de ventana gráfica especificado.

Capturando elementos

Además de las capturas de pantalla de página completa, podemos capturar capturas de pantalla de elementos específicos usando element.screenshot().

// Get reference to element
const menu = await page.$(‘.main-menu‘);

// Screenshot just that element
await menu.screenshot({path: ‘menu.png‘});

El elemento se desplazará a la vista antes de capturar la captura de pantalla. Esto permite capturar tomas de elementos que podrían estar fuera de la pantalla sin tener que desplazarse hasta ellos.

Algunos casos de uso para capturas de pantalla de elementos:

  • Capturar capturas de pantalla de componentes dinámicos como tickers o animaciones.
  • Depurar problemas de diseño tomando fotografías de elementos individuales.
  • Obtención de recursos de imagen de iconos e ilustraciones.

Capturas de pantalla de elementos fuera de pantalla

Un problema común es que los elementos se oscurecen o se mueven al intentar realizar capturas de pantalla durante las interacciones.

Podemos aprovechar el desplazamiento automático de elementos en element.screenshot() para capturar elementos de forma fiable en cualquier estado, incluso fuera de la pantalla:

// Click button which hides the element 
await page.click(‘.toggle-menu‘);

// Menu is now hidden but we can still screenshot it
await menu.screenshot({path: ‘hidden-menu.png‘}); 

Esto permite realizar capturas de pantalla fácilmente sin restablecer el estado de la página.

Esperando a que se cargue el contenido dinámico

Cuando trabajamos con páginas dinámicas, queremos esperar a que se procese el contenido antes de tomar capturas de pantalla para capturar el estado deseado.

Aquí hay un ejemplo esperando a que aparezca un elemento:

// Click button to trigger ajax call
await page.click(‘.load-content‘);

// Wait for new content to load
await page.waitForSelector(‘.loaded‘);

// Screenshot after loaded
await page.screenshot({path: ‘loaded.png‘}); 

page.waitForSelector() espera hasta que el selector exista en el DOM antes de continuar.

Algunas otras esperas útiles incluyen:

  • page.waitFor() – Esperar a que se cumpla una determinada condición.
  • page.waitForFunction() – Espere a que se completen las actualizaciones asíncronas de DOM.
  • page.waitUntil() – Espere hasta que se produzca la navegación.

La clave es elegir la condición de espera adecuada para la actualización de la página que desea capturar en una captura de pantalla.

Esperando cambios DOM específicos

Para sincronizar con cambios DOM más discretos, podemos esperar a que se actualicen los atributos en lugar de los selectores generales:

// Wait for text content to change
await page.waitForFunction(() => {
  return document.querySelector(‘.status‘).textContent === ‘Loaded‘; 
});

// Element updated  
await page.screenshot({/*...*/});

Este enfoque funciona bien para esperar a que se carguen datos clave en lugar de cambios estáticos del DOM.

Manejo de aplicaciones de página única (SPA)

Esperar cambios de DOM puede ser complicado con SPA de JavaScript complejos que actualizan el estado sin recargar.

Algunos consejos para manejarlos:

  • Espere a que la red esté inactiva después de las interacciones para permitir que se completen los XHR.
  • Espere a que desaparezcan componentes específicos, como superposiciones, en lugar de selectores generales.
  • Desplácese hasta la sección necesaria para forzar el renderizado antes de tomar la captura de pantalla.
  • Utilice esperas incrementales en lugar de tiempos de espera fijos.

Ningún enfoque funciona perfectamente para todos los SPA. Tendrás que experimentar con la aplicación en cuestión.

Desplazarse por las páginas antes de tomar capturas de pantalla de página completa

Para las páginas que requieren desplazamiento, necesitaremos desplazarnos mediante programación antes de tomar una captura de pantalla completa con fullPage: true.

Aquí hay un enfoque confiable:

await page.evaluate(() => {
  // Scroll to bottom
  window.scrollTo(0, document.body.scrollHeight);
}); 

// Capture full scrolled screenshot  
await page.screenshot({fullPage: true});

Esto desplaza la página hacia abajo hasta la posición de desplazamiento máximo antes de tomar la captura de pantalla.

Una alternativa es usar window.scrollBy() para desplazarse incrementalmente una cierta cantidad a la vez. Esto permite tomar capturas de pantalla continuas mientras se desplaza hacia abajo a lo largo de la página.

Manejo de páginas largas desplazables

Para páginas extremadamente largas, desplazarse por toda la longitud de una sola vez aún podría exceder los límites de memoria o tiempo.

Una buena solución es dividirlo en secciones, desplazarse poco a poco, tomar capturas de pantalla y unirlas:

const screenshots = [];

while (hasMoreContent()) {

  await page.evaluate(scrollDown);

  screenshots.push(await page.screenshot()); 

}

// Stitch screenshots together into one tall image

Esto evita tener que almacenar en el búfer la altura completa de la página en la memoria.

Desplazarse horizontalmente también

Para páginas con desplazamiento horizontal, podemos ajustar la secuencia de desplazamiento para que también se desplace horizontalmente:

await page.evaluate(() => {
  window.scrollTo(
    document.body.scrollWidth, 
    document.body.scrollHeight
  );
});

await page.screenshot({fullPage: true});

¡Esto captura el ancho y alto completo de la página!

Mejores prácticas para capturas de pantalla confiables

A continuación se ofrecen algunos consejos clave para realizar capturas de pantalla consistentes y confiables con Puppeteer:

Espere a que la red esté inactiva - Utilizar page.waitForNetworkIdle() después de las interacciones para garantizar que todas las solicitudes asíncronas se completen antes de capturar el estado.

Utilice esperas adecuadas – Elija esperas condicionales que se sincronicen con el estado de página deseado en lugar de tiempos de espera generales.

Establecer tamaño de ventana gráfica – Configure explícitamente la ventana gráfica para capturar capturas de pantalla precisas del dispositivo.

Proteger de animaciones/ventanas emergentes – Los elementos flotantes pueden provocar cambios – utilizar page.evaluate() para evitar efectos secundarios.

Deje tiempo para renderizar – Espere unos cientos de milisegundos después de desplazarse hasta que las páginas terminen de renderizarse antes de realizar las capturas de pantalla.

Estabilizar las pruebas escamosas – Establezca un ciclo de reintento con esperas alrededor de los pasos de la captura de pantalla para manejar las escamas.

Comparar con el bien conocido – Aproveche las herramientas de prueba de regresión visual para detectar cambios no deseados.

Conclusión

Espero que esta guía proporcione una descripción general completa de cómo tomar capturas de pantalla de elementos y páginas completas con Puppeteer para sus necesidades de web scraping.

Algunos temas clave que cubrimos:

  • Usando page.screenshot() y element.screenshot() para capturar capturas de pantalla
  • Opciones para controlar el tipo, la calidad y el formato de la imagen.
  • Desplazarse por las páginas y esperar contenido dinámico
  • Configuración del tamaño de la ventana gráfica para páginas responsivas
  • Mejores prácticas para flujos de trabajo de captura de pantalla confiables

Las capturas de pantalla automatizadas son invaluables para depurar raspadores, realizar pruebas visuales y capturar estados dinámicos. ¡Añádelos a tu kit de herramientas de web scraping con Puppeteer!

Únase a la conversación

Su dirección de correo electrónico no será publicada. Las areas obligatorias están marcadas como requeridas *