Índice
- Apegarse a las reglas
- Seleccionar la tecnología adecuada
- Instalación de requisitos previos
- Construyendo nuestro raspador
- Optimizaciones avanzadas
Para muchas tareas de web scraping, un cliente HTTP es suficiente para extraer los datos de una página. Sin embargo, cuando se trata de sitios web dinámicos, un navegador headless a veces resulta indispensable. En este tutorial, crearemos un raspador web que puede raspar sitios web dinámicos basados en Node.js y Puppeteer.
Comencemos con una pequeña sección sobre lo que realmente significa el web scraping. Todos utilizamos el web scraping en nuestra vida cotidiana. Simplemente describe el proceso de extracción de información de un sitio web. Por lo tanto, si copias y pegas una receta de tu plato de fideos favorito de Internet en tu cuaderno personal, estás realizando web scraping .
Cuando utilizamos este término en la industria del software, normalmente nos referimos a la automatización de esta tarea manual mediante el uso de un software. Siguiendo con nuestro ejemplo anterior de “plato de fideos”, este proceso normalmente implica dos pasos:
- Obteniendo la página
Primero tenemos que descargar la página completa. Este paso es como abrir la página en su navegador web al realizar el scraping manualmente. - Analizando los datos
Ahora, tenemos que extraer la receta en el HTML del sitio web y convertirla a un formato legible por máquina como JSON o XML.
En el pasado, trabajé para muchas empresas como consultor de datos. Me sorprendió ver cuántas tareas de extracción, agregación y enriquecimiento de datos todavía se realizan manualmente, aunque podrían automatizarse fácilmente con solo unas pocas líneas de código. De eso se trata exactamente el web scraping para mí: extraer y normalizar piezas valiosas de información de un sitio web para impulsar otro proceso empresarial que genere valor.
Durante este tiempo, vi empresas utilizar el web scraping para todo tipo de casos de uso. Las empresas de inversión se centraron principalmente en recopilar datos alternativos, como reseñas de productos , información de precios o publicaciones en redes sociales para respaldar sus inversiones financieras.
He aquí un ejemplo. Un cliente se acercó a mí para recopilar datos de reseñas de productos para obtener una lista extensa de productos de varios sitios web de comercio electrónico, incluida la calificación, la ubicación del revisor y el texto de la reseña para cada reseña enviada. Los datos de los resultados permitieron al cliente identificar tendencias sobre la popularidad del producto en diferentes mercados. Este es un excelente ejemplo de cómo una única información aparentemente “inútil” puede volverse valiosa en comparación con una cantidad mayor.
Otras empresas aceleran su proceso de ventas mediante el uso de web scraping para la generación de leads . Este proceso generalmente implica extraer información de contacto como el número de teléfono, la dirección de correo electrónico y el nombre de contacto de una lista determinada de sitios web. Automatizar esta tarea les da a los equipos de ventas más tiempo para acercarse a los clientes potenciales. Por tanto, aumenta la eficiencia del proceso de ventas.
Apegarse a las reglas
En general, el web scraping de datos disponibles públicamente es legal, como lo confirma la jurisdicción del caso Linkedin vs. HiQ . Sin embargo, me he fijado un conjunto de reglas éticas que me gusta seguir cuando inicio un nuevo proyecto de web scraping. Esto incluye:
- Comprobando el archivo robots.txt.
Por lo general, contiene información clara sobre a qué partes del sitio el propietario de la página puede acceder mediante robots y raspadores y resalta las secciones a las que no se debe acceder. - Leyendo los términos y condiciones.
En comparación con el archivo robots.txt, esta información no está disponible con menos frecuencia, pero generalmente indica cómo tratan los raspadores de datos. - Raspado con velocidad moderada.
El scraping crea una carga del servidor en la infraestructura del sitio de destino. Dependiendo de lo que raspe y del nivel de concurrencia que esté operando su raspador, el tráfico puede causar problemas en la infraestructura del servidor del sitio de destino. Por supuesto, la capacidad del servidor juega un papel importante en esta ecuación. Por lo tanto, la velocidad de mi scraper es siempre un equilibrio entre la cantidad de datos que pretendo extraer y la popularidad del sitio de destino. Se puede encontrar este equilibrio respondiendo una sola pregunta: "¿La velocidad planificada cambiará significativamente el tráfico orgánico del sitio?". En los casos en los que no estoy seguro de la cantidad de tráfico natural de un sitio, utilizo herramientas como ahrefs para tener una idea aproximada.
Seleccionar la tecnología adecuada
De hecho, el scraping con un navegador sin cabeza es una de las tecnologías de menor rendimiento que puede utilizar, ya que tiene un gran impacto en su infraestructura. Un núcleo del procesador de su máquina puede manejar aproximadamente una instancia de Chrome.
Hagamos un cálculo de ejemplo rápido para ver qué significa esto para un proyecto de web scraping del mundo real.
Guión
- Quieres extraer 20.000 URL.
- El tiempo medio de respuesta del sitio de destino es de 6 segundos.
- Su servidor tiene 2 núcleos de CPU.
El proyecto tardará 16 horas en completarse.
Por lo tanto, siempre trato de evitar el uso de un navegador cuando realizo una prueba de viabilidad de scraping para un sitio web dinámico.
Aquí hay una pequeña lista de verificación que siempre reviso:
- ¿Puedo forzar el estado de la página requerido a través de parámetros GET en la URL? En caso afirmativo, simplemente podemos ejecutar una solicitud HTTP con los parámetros adjuntos.
- ¿La información dinámica forma parte de la fuente de la página y está disponible a través de un objeto JavaScript en algún lugar del DOM? En caso afirmativo, podemos volver a utilizar una solicitud HTTP normal y analizar los datos del objeto encadenado.
- ¿Se obtienen los datos a través de una solicitud XHR? Si es así, ¿puedo acceder directamente al punto final con un cliente HTTP? En caso afirmativo, podemos enviar una solicitud HTTP al punto final directamente. Muchas veces, la respuesta incluso está formateada en JSON, lo que nos hace la vida mucho más fácil.
Si todas las preguntas se responden con un rotundo "No", oficialmente nos quedamos sin opciones viables para utilizar un cliente HTTP. Por supuesto, es posible que podamos probar más ajustes específicos del sitio, pero normalmente el tiempo necesario para descubrirlos es demasiado alto, en comparación con el rendimiento más lento de un navegador sin cabeza. Lo bueno de raspar con un navegador es que puedes raspar cualquier cosa que esté sujeta a la siguiente regla básica:
Si puede acceder a él con un navegador, puede eliminarlo.
Tomemos el siguiente sitio como ejemplo de nuestro raspador: https://quotes.toscrape.com/search.aspx . Incluye citas de una lista de autores determinados para una lista de temas. Todos los datos se obtienen a través de XHR.
Quien haya observado de cerca el funcionamiento del sitio y haya revisado la lista de verificación anterior probablemente se haya dado cuenta de que las cotizaciones en realidad se pueden eliminar utilizando un cliente HTTP, ya que se pueden recuperar realizando una solicitud POST directamente en el punto final de las cotizaciones. Pero dado que se supone que este tutorial cubre cómo raspar un sitio web usando Puppeteer, haremos como si esto fuera imposible.
Instalación de requisitos previos
Dado que vamos a compilar todo usando Node.js, primero creemos y abramos una nueva carpeta y creemos un nuevo proyecto de Nodo dentro, ejecutando el siguiente comando:
mkdir js-webscrapercd js-webscrapernpm init
Asegúrese de haber instalado npm. El instalador nos hará algunas preguntas sobre metainformación sobre este proyecto, que todos podemos omitir, presionando Enter.
Instalación del titiritero
Hemos estado hablando antes sobre el scraping con un navegador. Puppeteer es una API de Node.js que nos permite hablar con una instancia de Chrome sin cabeza mediante programación.
Instalémoslo usando npm:
npm install puppeteer
Construyendo nuestro raspador
Ahora, comencemos a construir nuestro raspador creando un nuevo archivo, llamado scraper.js .
Primero, importamos la biblioteca Puppeteer previamente instalada:
const puppeteer = require('puppeteer');
Como siguiente paso, le decimos a Puppeteer que abra una nueva instancia del navegador dentro de una función asincrónica y autoejecutable:
(async function scrape() { const browser = await puppeteer.launch({ headless: false }); // scraping logic comes here…})();
Nota : De forma predeterminada, el modo sin cabeza está desactivado, ya que esto aumenta el rendimiento. Sin embargo, cuando construyo un nuevo raspador, me gusta desactivar el modo sin cabeza. Esto nos permite seguir el proceso por el que pasa el navegador y ver todo el contenido renderizado. Esto nos ayudará a depurar nuestro script más adelante.
Dentro de nuestra instancia de navegador abierta, ahora abrimos una nueva página y la dirigimos hacia nuestra URL de destino: Muestras gratis y regalos
const page = await browser.newPage();await page.goto('https://quotes.toscrape.com/search.aspx');
Como parte de la función asincrónica, usaremos la await
declaración para esperar a que se ejecute el siguiente comando antes de continuar con la siguiente línea de código.
Ahora que hemos abierto con éxito una ventana del navegador y navegado a la página, tenemos que crear el estado del sitio web , de modo que la información deseada sea visible para el scraping.
Los temas disponibles se generan dinámicamente para un autor seleccionado. Por lo tanto, primero seleccionaremos 'Albert Einstein' y esperaremos la lista de temas generada. Una vez generada la lista por completo, seleccionamos 'aprendizaje' como tema y lo seleccionamos como segundo parámetro del formulario. Luego hacemos clic en enviar y extraemos las cotizaciones recuperadas del contenedor que contiene los resultados.
Como ahora convertiremos esto a lógica de JavaScript, primero hagamos una lista de todos los selectores de elementos de los que hemos hablado en el párrafo anterior:
Campo de selección de autor | #author |
Campo de selección de etiqueta | #tag |
Botón de enviar | input[type="submit"] |
Contenedor de cotización | .quote |
Antes de comenzar a interactuar con la página, nos aseguraremos de que todos los elementos a los que accederemos sean visibles, agregando las siguientes líneas a nuestro script:
await page.waitForSelector('#author');await page.waitForSelector('#tag');
A continuación, seleccionaremos valores para nuestros dos campos de selección:
await page.select('select#author', 'Albert Einstein');await page.select('select#tag', 'learning');
Ahora estamos listos para realizar nuestra búsqueda presionando el botón "Buscar" en la página y esperando a que aparezcan las citas:
await page.click('.btn');await page.waitForSelector('.quote');
Como ahora vamos a acceder a la estructura HTML DOM de la página, llamamos a la page.evaluate()
función proporcionada y seleccionamos el contenedor que contiene las comillas (es solo uno en este caso). Luego construimos un objeto y definimos nulo como el valor alternativo para cada object
parámetro:
let quotes = await page.evaluate(() = { let quotesElement = document.body.querySelectorAll('.quote'); let quotes = Object.values(quotesElement).map(x = { return { author: x.querySelector('.author').textContent ?? null, quote: x.querySelector('.content').textContent ?? null, tag: x.querySelector('.tag').textContent ?? null, };}); return quotes;});
Podemos hacer que todos los resultados sean visibles en nuestra consola registrándolos:
console.log(quotes);
Finalmente, cerremos nuestro navegador y agreguemos una declaración catch:
await browser.close();
El raspador completo se parece al siguiente:
const puppeteer = require('puppeteer');(async function scrape() { const browser = await puppeteer.launch({ headless: false }); const page = await browser.newPage(); await page.goto('https://quotes.toscrape.com/search.aspx'); await page.waitForSelector('#author'); await page.select('#author', 'Albert Einstein'); await page.waitForSelector('#tag'); await page.select('#tag', 'learning'); await page.click('.btn'); await page.waitForSelector('.quote'); // extracting information from code let quotes = await page.evaluate(() = { let quotesElement = document.body.querySelectorAll('.quote'); let quotes = Object.values(quotesElement).map(x = { return { author: x.querySelector('.author').textContent ?? null, quote: x.querySelector('.content').textContent ?? null, tag: x.querySelector('.tag').textContent ?? null, } }); return quotes; }); // logging results console.log(quotes); await browser.close();})();
Intentemos ejecutar nuestro raspador con:
node scraper.js
¡Y ahí vamos! El raspador devuelve nuestro objeto de cotización tal como se esperaba:
Optimizaciones avanzadas
Nuestro raspador básico ya está funcionando. Agreguemos algunas mejoras para prepararlo para tareas de scraping más serias.
Configurar un agente de usuario
De forma predeterminada, Puppeteer utiliza un agente de usuario que contiene la cadena HeadlessChrome
. Muchos sitios web buscan este tipo de firma y bloquean las solicitudes entrantes con una firma como esa. Para evitar que eso sea una posible razón por la que falle el raspador, siempre configuro un agente de usuario personalizado agregando la siguiente línea a nuestro código:
await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4298.0 Safari/537.36');
Esto podría mejorarse aún más eligiendo un agente de usuario aleatorio con cada solicitud de una serie de los 5 agentes de usuario más comunes. Puede encontrar una lista de los agentes de usuario más comunes en un artículo sobre Agentes de usuario más comunes .
Implementación de un proxy
Puppeteer hace que la conexión a un proxy sea muy fácil, ya que la dirección del proxy se puede pasar a Puppeteer en el inicio, de esta manera:
const browser = await puppeteer.launch({ headless: false, args: [ '--proxy-server=PROXY-ADDRESS' ]});
sslproxies proporciona una gran lista de servidores proxy gratuitos que puede utilizar. Alternativamente, se pueden utilizar servicios de proxy rotativos . Como los servidores proxy suelen compartirse entre muchos clientes (o usuarios gratuitos en este caso), la conexión se vuelve mucho menos fiable de lo que ya es en circunstancias normales. Este es el momento perfecto para hablar sobre manejo de errores y gestión de reintentos.
Gestión de errores y reintentos
Muchos factores pueden hacer que su raspador falle. Por lo tanto, es importante manejar los errores y decidir qué debería suceder en caso de falla. Dado que hemos conectado nuestro raspador a un proxy y esperamos que la conexión sea inestable (especialmente porque estamos usando servidores proxy gratuitos), queremos volver a intentarlo cuatro veces antes de rendirnos.
Además, no tiene sentido volver a intentar una solicitud con la misma dirección IP si anteriormente falló. Por lo tanto, vamos a construir un pequeño sistema de rotación de proxy .
En primer lugar, creamos dos nuevas variables:
let retry = 0;let maxRetries = 5;
Cada vez que ejecutamos nuestra función scrape()
, aumentaremos nuestra variable de reintento en 1. Luego envolvemos nuestra lógica de raspado completa con una declaración try and catch para que podamos manejar los errores. La gestión de reintentos ocurre dentro de nuestra catch
función:
La instancia anterior del navegador se cerrará y, si nuestra variable de reintento es más pequeña que nuestra maxRetries
variable, la función de raspado se llama de forma recursiva.
Nuestro raspador ahora se verá así:
const browser = await puppeteer.launch({ headless: false, args: ['--proxy-server=' + proxy]});try { const page = await browser.newPage(); … // our scraping logic} catch(e) { console.log(e); await browser.close(); if (retry maxRetries) { scrape(); }};
Ahora, agreguemos el rotador de proxy mencionado anteriormente.
Primero creemos una matriz que contenga una lista de servidores proxy:
let proxyList = [ '202.131.234.142:39330', '45.235.216.112:8080', '129.146.249.135:80', '148.251.20.79'];
Ahora, elige un valor aleatorio de la matriz:
var proxy = proxyList[Math.floor(Math.random() * proxyList.length)];
Ahora podemos ejecutar el proxy generado dinámicamente junto con nuestra instancia de Puppeteer:
const browser = await puppeteer.launch({ headless: false, args: ['--proxy-server=' + proxy]});
Por supuesto, este rotador de proxy podría optimizarse aún más para marcar servidores proxy inactivos, etc., pero esto definitivamente iría más allá del alcance de este tutorial.
Este es el código de nuestro scraper (incluidas todas las mejoras):
const puppeteer = require('puppeteer');// starting Puppeteerlet retry = 0;let maxRetries = 5;(async function scrape() { retry++; let proxyList = [ '202.131.234.142:39330', '45.235.216.112:8080', '129.146.249.135:80', '148.251.20.79' ]; var proxy = proxyList[Math.floor(Math.random() * proxyList.length)]; console.log('proxy: ' + proxy); const browser = await puppeteer.launch({ headless: false, args: ['--proxy-server=' + proxy] }); try { const page = await browser.newPage(); await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4298.0 Safari/537.36'); await page.goto('https://quotes.toscrape.com/search.aspx'); await page.waitForSelector('select#author'); await page.select('select#author', 'Albert Einstein'); await page.waitForSelector('#tag'); await page.select('select#tag', 'learning'); await page.click('.btn'); await page.waitForSelector('.quote'); // extracting information from code let quotes = await page.evaluate(() = { let quotesElement = document.body.querySelectorAll('.quote'); let quotes = Object.values(quotesElement).map(x = { return { author: x.querySelector('.author').textContent ?? null, quote: x.querySelector('.content').textContent ?? null, tag: x.querySelector('.tag').textContent ?? null, } }); return quotes; }); console.log(quotes); await browser.close(); } catch (e) { await browser.close(); if (retry maxRetries) { scrape(); } }})();
¡Voilá! Al ejecutar nuestro raspador dentro de nuestra terminal, se devolverán las cotizaciones.
El dramaturgo como alternativa al titiritero
Titiritero fue desarrollado por Google. A principios de 2020, Microsoft lanzó una alternativa llamada Playwright . Microsoft buscó a muchos ingenieros del Puppeteer-Team. Por lo tanto, Playwright fue desarrollado por muchos ingenieros que ya trabajaron en Puppeteer. Además de ser el chico nuevo en el blog, el mayor punto diferenciador de Playwright es la compatibilidad con varios navegadores, ya que es compatible con Chromium, Firefox y WebKit (Safari).
Las pruebas de rendimiento ( como esta realizada por Checkly) muestran que Puppeteer generalmente ofrece un rendimiento un 30% mejor, en comparación con Playwright, lo que coincide con mi propia experiencia, al menos en el momento de escribir este artículo.
Otras diferencias, como el hecho de que se pueden ejecutar varios dispositivos con una sola instancia del navegador, no son realmente valiosas para el contexto del web scraping.
Recursos y enlaces adicionales
- Documentación del titiritero
- Aprendizaje de titiritero y dramaturgo
- Web Scraping con Javascript por Zenscrape
- Agentes de usuario más comunes
- Titiritero versus dramaturgo
(vf, yk, il)Explora más en
- javascript
- Nodo.js
- Navegadores
- Sin cabeza
Tal vez te puede interesar:
- ¿Deberían abrirse los enlaces en ventanas nuevas?
- 24 excelentes tutoriales de AJAX
- 70 técnicas nuevas y útiles de AJAX y JavaScript
- Más de 45 excelentes recursos y repositorios de fragmentos de código
La guía para el scraping ético de sitios web dinámicos con Node.js y Puppeteer
Apegarse a las reglasSeleccionar la tecnología adecuadaInstalación de requisitos previosConstruyendo nuestro raspadorOptimizaciones avanzadasClase magistral
programar
es
https://aprendeprogramando.es/static/images/programar-la-guia-para-el-scraping-etico-de-sitios-web-dinamicos-con-node-1087-0.jpg
2024-12-03
Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente