Raspado web con Puppeteer

Esta guía paso a paso muestra cómo raspar sitios web estáticos y dinámicos utilizando Puppeteer
9 min read
web scraping with puppeteer

Puppeteer es una librería de testeo y automatización de navegadores que también es buena para raspado web. En comparación con herramientas más simples como Axios y cheerio, Puppeteer permite a los desarrolladores raspar contenido dinámico (es decir, contenido que cambia en función de las acciones del usuario).

Esto significa que se puede utilizar para raspar aplicaciones web (aplicaciones de una sola página) que cargan su contenido utilizando JavaScript, exactamente lo que haremos aquí.

Raspado web con Puppeteer  

En este tutorial, se mostrará cómo raspar datos estáticos y dinámicos (es decir, títulos de publicaciones y enlaces del Blog de Bright Data) con Puppeteer.  

Configuración

 

Antes de comenzar el tutorial, es necesario asegurarse de que tiene Node.js instalado en su computadora. Se puede descargar desde su página oficial de descargas.

A continuación, se crea un nuevo directorio para el proyecto y se navega hasta él con los siguientes comandos:

mkdir puppeteer_tutorial 
cd puppeteer_tutorial 
npm init -y 

Luego, instale Puppeteer con este comando:

npm i puppeteer --save

Este comando también descarga un navegador dedicado que usará la librería.

Raspado de un sitio estático

 

Como todas las herramientas de raspado web, Puppeteer le permite raspar el código HTML de las páginas web.

A continuación se muestran los pasos que puede seguir para utilizar Puppeteer para raspar la primera página de entradas del blog de Bright Data:

Cree un archivo index.js e importe Puppeteer:

const puppeteer = require('puppeteer');

Luego inserte el boilerplate necesario para ejecutar Puppeteer:

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: null
  });

  const page = await browser.newPage();
  await page.goto('https://brightdata.com/blog');

  // all the web scraping will happen here  

  await browser.close();

})();

Esta función abre un navegador, navega hasta la página para rasparla y cierra el navegador.

Todo lo que queda por hacer es raspar los datos de la página.

En Puppeteer, la forma más fácil de acceder a los datos HTML es a través del método page.evaluate. Aunque Puppeteer tiene metodos $ y $$, que son envoltorios útiles para obtener elementos, es mas simple obtener todos los datos de page.evaluate.

En el blog de Bright Data, todos los datos de la entrada del blog están envueltos en una etiqueta <a> con la clase brd_post_entry. El título de la entrada está en un elemento <h3> con la clase brd_post_title. El enlace al post es el valor href de brd_post_entry

Este es el aspecto de una función page.evaluate que extrae esos valores:

  const data = await page.evaluate( () => {

    let data = [];
    const titles = document.querySelectorAll('.brd_post_entry');

    for (const title of titles) {
      const titleText = title.querySelector('.brd_post_title').textContent;
      const titleLink = title.href;

      const article = { title: titleText, link: titleLink };
      data.push(article);
    }

    return data;

  })

Por último, se puede imprimir los datos en la consola

  console.log(data);

El script completo tiene este aspecto

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: null
  });

  const page = await browser.newPage();
  await page.goto('https://brightdata.com/blog');

  const data = await page.evaluate(() => {

    let data = [];
    const titles = document.querySelectorAll('.brd_post_entry');

    for (const title of titles) {
      const titleText = title.querySelector('.brd_post_title').textContent;
      const titleLink = title.href;

      const article = { title: titleText, link: titleLink };
      data.push(article);
    }

    return data;

  })

  console.log(data);

  await browser.close();

})();

Ejecútelo llamando a node index.js en la terminal. El script debería devolver una lista de títulos de entradas y enlaces.

[
  {
    title: 'APIs for Dummies: Learning About APIs',
    link: 'https://brightdata.com/blog/web-data/apis-for-dummies'
  },
  {
    title: 'Guide to Using cURL with Python',
    link: 'https://brightdata.com/blog/how-tos/curl-with-python'
  },
  {
    title: 'Guide to Scraping Walmart',
    link: 'https://brightdata.com/blog/how-tos/guide-to-scraping-walmart'
  },
…

Raspado de contenido dinámico

 

Raspar contenido estático es una tarea sencilla que se puede hacer fácilmente con herramientas sin complicaciones. Afortunadamente, Puppeteer se puede utilizar para llevar a cabo una amplia gama de acciones, como hacer clic, escribir y desplazarse. Puede utilizar todo esto para interactuar con páginas dinámicas y simular las acciones del usuario.

Una tarea común de raspado web con una biblioteca como esta sería buscar un determinado conjunto de datos en la página. Por ejemplo, es posible que desee utilizar Puppeteer para buscar todos los posts de Bright Data sobre Puppeteer.

A continuación se explica cómo hacerlo:

Paso 1: Aceptar cookies

 

Cuando una persona visita el blog de Bright Data, a veces aparece un banner de cookies:

Para ello, debe hacer clic en el botón Aceptar todo con el siguiente código:  

  await page.waitForSelector('#brd_cookies_bar_accept', {timeout: 5000})
    .then(element => element.click())
    .catch(error => console.log(error));

La primera línea del código espera a que aparezca un elemento con la etiqueta #brd_cookies_bar_accept durante 5 segundos. La segunda línea hace clic en ese elemento. La tercera línea se asegura de que el script no se bloquee si la barra de cookies no aparece.

Tenga en cuenta que la espera en Puppeteer se produce proporcionando alguna condición que desea esperar, no estableciendo un tiempo de espera determinado para que se ejecute la acción anterior. Lo primero se llama espera implícita, y lo segundo espera explícita.

No se recomendada la espera explícita en Puppeteer, ya que conduce a problemas de ejecución. Si proporciona un tiempo explícito de espera, es probable que sea demasiado largo (lo que es ineficiente) o demasiado corto (lo que significa que el script no se ejecutará correctamente).

Paso 2: Buscar Entradas

 

Después, el script necesita hacer clic en el icono de búsqueda. Escribe “Puppeteer” y pulsa el icono de búsqueda de nuevo para activar la búsqueda:

Esto se puede hacer con el siguiente código:


await page.click('.search_icon');

  await page.waitForSelector('.search_container.active');
  const search_form = await page.waitForSelector('#blog_search');
  await search_form.type('puppeteer');

 await page.click('.search_icon');

  await new Promise(r => setTimeout(r, 2000));

Este ejemplo funciona de forma similar al ejemplo del banner de cookies. Después de pulsar el botón, es preciso esperar a que aparezca el contenedor de búsqueda, razón por la cual el código espera un elemento que coincida con el selector CSS de .search_container.active.

Adicionalmente, al final, se necesita añadir una parada de dos segundos para que los elementos se carguen. Mientras que se desaconsejan las esperas explícitas en Puppeteer, no hay otras buenas opciones aquí ahora mismo.

En la mayoría de los sitios web, si se produce un cambio en la URL, puede utilizar el método waitForNavigation. Si aparece un nuevo elemento, se puede usar el método waitForSelector. Es un poco más difícil averiguar si algunos elementos se actualizan y está fuera del alcance de este artículo.

Si quiere intentarlo por su cuenta, esta respuesta de Stack Overflow puede ser de ayuda.

Paso 3: Recopilar las publicaciones

 

Después de buscar las entradas, puede utilizar el código que ya se usó para el raspado de páginas estáticas, para obtener los títulos de las entradas del blog:

  const data = await page.evaluate( () => {

    let data = [];
    const titles = document.querySelectorAll('.brd_post_entry');

    for (const title of titles) {
      const titleText = title.querySelector('.brd_post_title').textContent;
      const titleLink = title.href;

      const article = { title: titleText, link: titleLink };
      data.push(article);
    }

    return data;

  })

  console.log(data);

Aquí está el código completo del script:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: null
  });

  const page = await browser.newPage();
  await page.goto('https://brightdata.com/blog');

  const cookie_bar_accept = await page.waitForSelector('#brd_cookies_bar_accept');
  await cookie_bar_accept.click();
  await new Promise(r => setTimeout(r, 500));

  await page.click('.search_icon');

  await page.waitForSelector('.search_container.active');
  const search_form = await page.waitForSelector('#blog_search');
  await search_form.type('puppeteer');

  await page.click('.search_icon');

  await new Promise(r => setTimeout(r, 2000));

  const data = await page.evaluate( () => {

    let data = [];
    const titles = document.querySelectorAll('.brd_post_entry');

    for (const title of titles) {
      const titleText = title.querySelector('.brd_post_title').textContent;
      const titleLink = title.href;

      const article = { title: titleText, link: titleLink };
      data.push(article);
    }

    return data;

  })

  console.log(data);

  await browser.close();

})();

¿Puede hacerlo mejor?

 

Aunque es posible raspar páginas web con Puppeteer, no es lo ideal. Puppeteer está hecho para la automatización de pruebas, lo que lo hace un poco incómodo para llevar a cabo el raspado web.

Por ejemplo, si busca escala y eficiencia en sus scripts, es importante ser capaz de raspar sin que le bloqueen. Para hacer esto, puede usar proxies-gateways entre usted y el sitio web que raspa. Aunque Puppeteer soporta el uso de proxies, necesita encontrar y contratar una red de proxies por su cuenta (aprenda más sobre la integración de proxies de Puppeteer con Bright Data).

Además, optimizar Puppeteer para un uso paralelo no es fácil. Si desea raspar una gran cantidad de datos, tendrá que trabajar duro para obtener un rendimiento óptimo.

Estas desventajas significan que Puppeteer es una buena opción para pequeños scripts para uso de hobby, pero tomará mucho tiempo escalar sus operaciones si lo usa.

En caso de que desee algo que sea más fácil de usar, puede elegir una plataforma de datos web como Bright Data. Permite a las empresas recopilar cantidades masivas de datos estructurados de la web utilizando herramientas fáciles de usar, como Scraping Browser (compatible con Puppeteer/Playwright), que está especialmente diseñado para el raspado.  

Conclusión

Este artículo ha mostrado cómo utilizar Puppeteer para raspar páginas web estáticas y dinámicas.

Puppeteer puede ejecutar la mayoría de las acciones que un navegador puede hacer, incluyendo hacer clic en elementos, escribir texto y ejecutar JavaScript. Y debido al uso de esperas implícitas, los scripts escritos en Puppeteer son rápidos y fáciles de escribir.

Pero también puede haber algunos problemas: Puppeteer no es la herramienta más eficaz para el raspado web y su documentación no está bien adaptada para principiantes. También es difícil escalar las operaciones de raspado utilizando Puppeteer si no se tiene experiencia en su uso.

¿Cansado de raspar datos por cuenta propia? Obtenga conjuntos de datos pre recopilados o personalizados.