Scraping BrowserGuíasOptimización del costo

Soluciones de optimización del tráfico proxy del navegador de scraping

Introducción

Cuando se utiliza Puppeteer para el scraping de datos, el consumo de tráfico es una consideración importante. Especialmente cuando se utilizan servicios proxy, los costos de tráfico pueden aumentar significativamente. Para optimizar el uso del tráfico, podemos adoptar las siguientes estrategias:

  1. Intercepción de recursos: Reducir el consumo de tráfico interceptando las solicitudes de recursos innecesarias.
  2. Intercepción de URL de solicitud: Reducir aún más el tráfico interceptando solicitudes específicas según las características de la URL.
  3. Simular dispositivos móviles: Usar configuraciones de dispositivos móviles para obtener versiones de páginas más ligeras.
  4. Optimización integral: Combinar los métodos anteriores para lograr los mejores resultados.

Esquema de optimización 1: Intercepción de recursos

Introducción a la intercepción de recursos

En Puppeteer, page.setRequestInterception(true) puede capturar cada solicitud de red iniciada por el navegador y decidir continuar (request.continue()), terminar (request.abort()) o personalizar la respuesta (request.respond()).

Este método puede reducir significativamente el consumo de ancho de banda, especialmente adecuado para escenarios de crawling, captura de pantalla y optimización del rendimiento.

Tipos de recursos interceptables y sugerencias

Tipo de recursoDescripciónEjemploImpacto después de la intercepciónRecomendación
imageRecursos de imagenImágenes JPG/PNG/GIF/WebPLas imágenes no se mostrarán⭐ Seguro
fontArchivos de fuenteFuentes TTF/WOFF/WOFF2Se usarán las fuentes predeterminadas del sistema⭐ Seguro
mediaArchivos multimediaArchivos de vídeo/audioNo se puede reproducir el contenido multimedia⭐ Seguro
manifestManifiesto de aplicación webArchivo de configuración PWALa funcionalidad PWA puede verse afectada⭐ Seguro
prefetchRecursos prefetch<link rel="prefetch">Impacto mínimo en la página⭐ Seguro
stylesheetHoja de estilo CSSArchivos CSS externosSe pierden los estilos de la página, puede afectar el diseño⚠️ Precaución
websocketWebSocketConexión de comunicación en tiempo realFuncionalidad en tiempo real deshabilitada⚠️ Precaución
eventsourceEventos enviados por el servidorDatos push del servidorFuncionalidad push deshabilitada⚠️ Precaución
preflightSolicitud de preflight CORSSolicitud OPTIONSFallan las solicitudes de origen cruzado⚠️ Precaución
scriptScripts JavaScriptArchivos JS externosFuncionalidad dinámica deshabilitada, la SPA puede no renderizar❌ Evitar
xhrSolicitudes XHRSolicitudes de datos AJAXNo se pueden obtener datos dinámicos❌ Evitar
fetchSolicitudes FetchSolicitudes AJAX modernasNo se pueden obtener datos dinámicos❌ Evitar
documentDocumento principalPágina HTML en síLa página no se puede cargar❌ Evitar

Explicación del nivel de recomendación:

  • Seguro: La intercepción casi no tiene impacto en el scraping de datos o la representación de la primera pantalla; se recomienda bloquear de forma predeterminada.
  • ⚠️ Precaución: Puede interrumpir los estilos, las funciones en tiempo real o las solicitudes de origen cruzado; requiere juicio comercial.
  • Evitar: Alta probabilidad de que las páginas SPA/dinámicas no puedan renderizar u obtener datos normalmente, a menos que esté absolutamente seguro de que no necesita estos recursos.

Ejemplo de código de intercepción de recursos

import puppeteer from 'puppeteer-core';
 
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
 
async function scrapeWithResourceBlocking(url) {
    const browser = await puppeteer.connect({
        browserWSEndpoint: scrapelessUrl,
        defaultViewport: null
    });
    const page = await browser.newPage();
 
    // Habilitar la intercepción de solicitudes
    await page.setRequestInterception(true);
 
    // Definir los tipos de recursos que se bloquearán
    const BLOCKED_TYPES = new Set([
        'image',
        'font',
        'media',
        'stylesheet',
    ]);
 
    // Interceptar solicitudes
    page.on('request', (request) => {
        if (BLOCKED_TYPES.has(request.resourceType())) {
            request.abort();
            console.log(`Bloqueado: ${request.resourceType()} - ${request.url().substring(0, 50)}...`);
        } else {
            request.continue();
        }
    });
 
    await page.goto(url, {waitUntil: 'domcontentloaded'});
 
    // Extraer datos
    const data = await page.evaluate(() => {
        return {
            title: document.title,
            content: document.body.innerText.substring(0, 1000)
        };
    });
 
    await browser.close();
    return data;
}
 
// Uso
scrapeWithResourceBlocking('https://www.scrapeless.com')
    .then(data => console.log('Resultado del scraping:', data))
    .catch(error => console.error('Error en el scraping:', error));

Esquema de optimización 2: Intercepción de URL de solicitud

Además de interceptar por tipo de recurso, se puede realizar un control de intercepción más granular en función de las características de la URL. Esto es particularmente eficaz para bloquear anuncios, scripts de análisis y otras solicitudes de terceros innecesarias.

Estrategias de intercepción de URL

  1. Interceptar por dominio: Bloquear todas las solicitudes de un dominio específico
  2. Interceptar por ruta: Bloquear solicitudes de una ruta específica
  3. Interceptar por tipo de archivo: Bloquear archivos con extensiones específicas
  4. Interceptar por palabra clave: Bloquear solicitudes cuyas URL contengan palabras clave específicas

Patrones de URL interceptables comunes

Patrón de URLDescripciónEjemploRecomendación
Servicios de publicidadDominios de red publicitariaad.doubleclick.net, googleadservices.com⭐ Seguro
Servicios de análisisScripts de estadísticas y análisisgoogle-analytics.com, hotjar.com⭐ Seguro
Complementos de redes socialesBotones para compartir en redes sociales, etc.platform.twitter.com, connect.facebook.net⭐ Seguro
Pixels de seguimientoPixels que rastrean el comportamiento del usuarioURL que contienen pixel, beacon, tracker⭐ Seguro
Archivos multimedia grandesArchivos de vídeo y audio grandesExtensiones como .mp4, .webm, .mp3⭐ Seguro
Servicios de fuentesServicios de fuentes onlinefonts.googleapis.com, use.typekit.net⭐ Seguro
Recursos CDNCDN de recursos estáticoscdn.jsdelivr.net, unpkg.com⚠️ Precaución

Ejemplo de código de intercepción de URL

import puppeteer from 'puppeteer-core';
 
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
 
async function scrapeWithUrlBlocking(url) {
    const browser = await puppeteer.connect({
        browserWSEndpoint: scrapelessUrl,
        defaultViewport: null
    });
    const page = await browser.newPage();
 
    // Habilitar la intercepción de solicitudes
    await page.setRequestInterception(true);
 
    // Definir los dominios y patrones de URL que se bloquearán
    const BLOCKED_DOMAINS = [
        'google-analytics.com',
        'googletagmanager.com',
        'doubleclick.net',
        'facebook.net',
        'twitter.com',
        'linkedin.com',
        'adservice.google.com',
    ];
 
    const BLOCKED_PATHS = [
        '/ads/',
        '/analytics/',
        '/pixel/',
        '/tracking/',
        '/stats/',
    ];
 
    // Interceptar solicitudes
    page.on('request', (request) => {
        const url = request.url();
 
        // Comprobar dominio
        if (BLOCKED_DOMAINS.some(domain => url.includes(domain))) {
            request.abort();
            console.log(`Dominio bloqueado: ${url.substring(0, 50)}...`);
            return;
        }
 
        // Comprobar ruta
        if (BLOCKED_PATHS.some(path => url.includes(path))) {
            request.abort();
            console.log(`Ruta bloqueada: ${url.substring(0, 50)}...`);
            return;
        }
 
        // Permitir otras solicitudes
        request.continue();
    });
 
    await page.goto(url, {waitUntil: 'domcontentloaded'});
 
    // Extraer datos
    const data = await page.evaluate(() => {
        return {
            title: document.title,
            content: document.body.innerText.substring(0, 1000)
        };
    });
 
    await browser.close();
    return data;
}
 
// Uso
scrapeWithUrlBlocking('https://www.scrapeless.com')
    .then(data => console.log('Resultado del scraping:', data))
    .catch(error => console.error('Error en el scraping:', error));

Esquema de optimización 3: Simular dispositivos móviles

Simular dispositivos móviles es otra estrategia eficaz de optimización del tráfico porque los sitios web móviles suelen proporcionar un contenido de página más ligero.

Ventajas de la simulación de dispositivos móviles

  1. Versiones de página más ligeras: Muchos sitios web proporcionan un contenido más conciso para los dispositivos móviles
  2. Recursos de imagen más pequeños: Las versiones móviles suelen cargar imágenes más pequeñas
  3. CSS y JavaScript simplificados: Las versiones móviles suelen utilizar estilos y scripts simplificados
  4. Anuncios y contenido no esencial reducidos: Las versiones móviles a menudo eliminan algunas funcionalidades no esenciales
  5. Respuesta adaptativa: Obtener diseños de contenido optimizados para pantallas pequeñas

Configuración de la simulación de dispositivos móviles

Aquí están los parámetros de configuración para varios dispositivos móviles de uso común:

const iPhoneX = {
    viewport: {
        width: 375,
        height: 812,
        deviceScaleFactor: 3,
        isMobile: true,
        hasTouch: true,
        isLandscape: false
    }
};

O usar directamente los métodos integrados de puppeteer para simular dispositivos móviles

import { KnownDevices } from 'puppeteer-core';
const iPhone = KnownDevices['iPhone 15 Pro'];
 
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.emulate(iPhone);

Ejemplo de código de simulación de dispositivos móviles

import puppeteer, {KnownDevices} from 'puppeteer-core';
 
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
 
async function scrapeWithMobileEmulation(url) {
    const browser = await puppeteer.connect({
        browserWSEndpoint: scrapelessUrl,
        defaultViewport: null
    });
 
    const page = await browser.newPage();
 
    // Establecer la simulación del dispositivo móvil
    const iPhone = KnownDevices['iPhone 15 Pro'];
    await page.emulate(iPhone);
 
    await page.goto(url, {waitUntil: 'domcontentloaded'});
    // Extraer datos
    const data = await page.evaluate(() => {
        return {
            title: document.title,
            content: document.body.innerText.substring(0, 1000)
        };
    });
 
    await browser.close();
    return data;
}
 
// Uso
scrapeWithMobileEmulation('https://www.scrapeless.com')
    .then(data => console.log('Resultado del scraping:', data))
    .catch(error => console.error('Error en el scraping:', error));

Ejemplo de optimización integral

Aquí hay un ejemplo integral que combina todos los esquemas de optimización:

import puppeteer, {KnownDevices} from 'puppeteer-core';
 
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
 
async function optimizedScraping(url) {
    console.log(`Comenzando el scraping optimizado: ${url}`);
 
    // Registrar el uso del tráfico
    let totalBytesUsed = 0;
 
    const browser = await puppeteer.connect({
        browserWSEndpoint: scrapelessUrl,
        defaultViewport: null
    });
 
    const page = await browser.newPage();
 
    // Establecer la simulación del dispositivo móvil
    const iPhone = KnownDevices['iPhone 15 Pro'];
    await page.emulate(iPhone);
 
    // Establecer la intercepción de solicitudes
    await page.setRequestInterception(true);
 
    // Definir los tipos de recursos que se bloquearán
    const BLOCKED_TYPES = [
        'image',
        'media',
        'font'
    ];
 
    // Definir los dominios que se bloquearán
    const BLOCKED_DOMAINS = [
        'google-analytics.com',
        'googletagmanager.com',
        'facebook.net',
        'doubleclick.net',
        'adservice.google.com'
    ];
 
    // Definir las rutas de URL que se bloquearán
    const BLOCKED_PATHS = [
        '/ads/',
        '/analytics/',
        '/tracking/'
    ];
 
    // Interceptar solicitudes
    page.on('request', (request) => {
        const url = request.url();
        const resourceType = request.resourceType();
 
        // Comprobar el tipo de recurso
        if (BLOCKED_TYPES.includes(resourceType)) {
            console.log(`Tipo de recurso bloqueado: ${resourceType} - ${url.substring(0, 50)}...`);
            request.abort();
            return;
        }
 
        // Comprobar dominio
        if (BLOCKED_DOMAINS.some(domain => url.includes(domain))) {
            console.log(`Dominio bloqueado: ${url.substring(0, 50)}...`);
            request.abort();
            return;
        }
 
        // Comprobar ruta
        if (BLOCKED_PATHS.some(path => url.includes(path))) {
            console.log(`Ruta bloqueada: ${url.substring(0, 50)}...`);
            request.abort();
            return;
        }
 
        // Permitir otras solicitudes
        request.continue();
    });
 
    // Supervisar el tráfico de red
    page.on('response', async (response) => {
        const headers = response.headers();
        const contentLength = headers['content-length'] ? parseInt(headers['content-length'], 10) : 0;
        totalBytesUsed += contentLength;
    });
 
    await page.goto(url, {waitUntil: 'domcontentloaded'});
 
    // Simular el desplazamiento para activar el contenido de carga diferida
    await page.evaluate(() => {
        window.scrollBy(0, window.innerHeight);
    });
 
    await new Promise(resolve => setTimeout(resolve, 1000))
 
    // Extraer datos
    const data = await page.evaluate(() => {
        return {
            title: document.title,
            content: document.body.innerText.substring(0, 1000),
            links: Array.from(document.querySelectorAll('a')).slice(0, 10).map(a => ({
                text: a.innerText,
                href: a.href
            }))
        };
    });
 
    // Mostrar estadísticas de uso del tráfico
    console.log(`\nEstadísticas de uso del tráfico:`);
    console.log(`Usado: ${(totalBytesUsed / 1024 / 1024).toFixed(2)} MB`);
 
    await browser.close();
    return data;
}
 
// Uso
optimizedScraping('https://www.scrapeless.com')
    .then(data => console.log('Scraping completo:', data))
    .catch(error => console.error('Error en el scraping:', error));

Comparación de optimización

Intentamos eliminar el código optimizado del ejemplo integral para comparar el tráfico antes y después de la optimización. Aquí está el código de ejemplo sin optimizar:

import puppeteer from 'puppeteer-core';
 
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
 
async function optimizedScraping(url) {
  console.log(`Comenzando el scraping optimizado: ${url}`);
 
  // Registrar el uso del tráfico
  let totalBytesUsed = 0;
 
  const browser = await puppeteer.connect({
    browserWSEndpoint: scrapelessUrl,
    defaultViewport: null
  });
 
  const page = await browser.newPage();
 
  // Establecer la intercepción de solicitudes
  await page.setRequestInterception(true);
 
  // Interceptar solicitudes
  page.on('request', (request) => {
    request.continue();
  });
 
  // Supervisar el tráfico de red
  page.on('response', async (response) => {
    const headers = response.headers();
    const contentLength = headers['content-length'] ? parseInt(headers['content-length'], 10) : 0;
    totalBytesUsed += contentLength;
  });
 
  await page.goto(url, {waitUntil: 'domcontentloaded'});
 
  // Simular el desplazamiento para activar el contenido de carga diferida
  await page.evaluate(() => {
    window.scrollBy(0, window.innerHeight);
  });
 
  await new Promise(resolve => setTimeout(resolve, 1000))
 
  // Extraer datos
  const data = await page.evaluate(() => {
    return {
      title: document.title,
      content: document.body.innerText.substring(0, 1000),
      links: Array.from(document.querySelectorAll('a')).slice(0, 10).map(a => ({
        text: a.innerText,
        href: a.href
      }))
    };
  });
 
  // Mostrar estadísticas de uso del tráfico
  console.log(`\nEstadísticas de uso del tráfico:`);
  console.log(`Usado: ${(totalBytesUsed / 1024 / 1024).toFixed(2)} MB`);
 
  await browser.close();
  return data;
}
 
// Uso
optimizedScraping('https://www.scrapeless.com')
  .then(data => console.log('Scraping completo:', data))
  .catch(error => console.error('Error en el scraping:', error));

Después de ejecutar el código sin optimizar, podemos ver la diferencia de tráfico de forma muy intuitiva a partir de la información impresa:

EscenarioTráfico usado (MB)Ratio de ahorro
Sin optimizar6.03
Optimizado0.81≈ 86.6 %

Al combinar los esquemas de optimización anteriores, se puede reducir significativamente el consumo de tráfico proxy, se puede mejorar la eficiencia del scraping y se puede garantizar que se obtenga el contenido principal requerido.