DocumentaciónNavegador y CrawlAgent BrowserOptimización de costos

Optimización de costos

Introducción

Al utilizar Puppeteer para la extracción de datos, el consumo de tráfico es un aspecto importante a considerar. Especialmente al usar servicios de 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 solicitudes de recursos innecesarios.
  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: Utilizar configuraciones de dispositivos móviles para obtener versiones más ligeras de las páginas.
  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 si 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 rastreo, captura de pantalla y optimización de rendimiento .

Tipos de recursos interceptables y recomendaciones

Tipo de recursoDescripciónEjemploImpacto tras la interceptaciónRecomendación
imageRecursos de imagenImágenes JPG/PNG/GIF/WebPLas imágenes no se mostrarán⭐ Seguro
fontArchivos de fuentesFuentes TTF/WOFF/WOFF2Se utilizarán las fuentes predeterminadas del sistema⭐ Seguro
mediaArchivos multimediaArchivos de video/audioEl contenido multimedia no se podrá reproducir⭐ Seguro
manifestWeb App ManifestArchivo de configuración PWALa funcionalidad PWA podría verse afectada⭐ Seguro
prefetchRecursos de prefijado<link rel="prefetch">Impacto mínimo en la página⭐ Seguro
stylesheetHoja de estilos CSSArchivos CSS externosSe pierden los estilos de la página, podría afectar al diseño⚠️ Precaución
websocketWebSocketConexión de comunicación en tiempo realLa funcionalidad en tiempo real queda deshabilitada⚠️ Precaución
eventsourceServer-Sent EventsDatos enviados por el servidorLa funcionalidad de envío queda deshabilitada⚠️ Precaución
preflightSolicitud previa CORSSolicitud OPTIONSLas solicitudes de origen cruzado fallan⚠️ Precaución
scriptScripts de JavaScriptArchivos JS externosFuncionalidad dinámica deshabilitada, es posible que las SPA no se rendericen❌ Evitar
xhrSolicitudes XHRSolicitudes de datos AJAXImposible obtener datos dinámicos❌ Evitar
fetchSolicitudes fetchSolicitudes AJAX modernasImposible obtener datos dinámicos❌ Evitar
documentDocumento principalLa propia página HTMLLa página no puede cargarse❌ Evitar

Nivel de recomendación Explicación:

  • ⭐ Seguro: La intercepción tiene casi ningún impacto en la extracción de datos o en el renderizado de la primera pantalla; se recomienda bloquear por defecto.
  • ⚠️ Precaución: Puede romper estilos, funciones en tiempo real o solicitudes de origen cruzado; requiere juicio según el caso de uso.
  • ❌ Evitar: Alta probabilidad de provocar fallos en la renderización de SPAs o sitios dinámicos, o impedir la obtención normal de datos, a menos que esté absolutamente seguro de que no necesita estos recursos.

Código de ejemplo de intercepción de recursos

import { Scrapeless } from '@scrapeless-ai/sdk';
import puppeteer from 'puppeteer-core';
 
const client = new Scrapeless({ apiKey: 'API Key' });
 
const { browserWSEndpoint } = client.browser.create({
    sessionName: 'sdk_test',
    sessionTTL: 180,
    proxyCountry: 'ANY',
    sessionRecording: true,
    fingerprint,
});
 
async function scrapeWithResourceBlocking(url) {
    const browser = await puppeteer.connect({
        browserWSEndpoint,
        defaultViewport: null
    });
    const page = await browser.newPage();
 
    // Enable request interception
    await page.setRequestInterception(true);
 
    // Define resource types to block
    const BLOCKED_TYPES = new Set([
        'image',
        'font',
        'media',
        'stylesheet',
    ]);
 
    // Intercept requests
    page.on('request', (request) => {
        if (BLOCKED_TYPES.has(request.resourceType())) {
            request.abort();
            console.log(`Blocked: ${request.resourceType()} - ${request.url().substring(0, 50)}...`);
        } else {
            request.continue();
        }
    });
 
    await page.goto(url, {waitUntil: 'domcontentloaded'});
 
    // Extract data
    const data = await page.evaluate(() => {
        return {
            title: document.title,
            content: document.body.innerText.substring(0, 1000)
        };
    });
 
    await browser.close();
    return data;
}
 
// Usage
scrapeWithResourceBlocking('https://www.scrapeless.com')
    .then(data => console.log('Scraping result:', data))
    .catch(error => console.error('Scraping failed:', error));

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

Además de intercepción por tipo de recurso, se puede realizar un control más granular basado en características de la URL. Esto es particularmente efectivo para bloquear anuncios, scripts analíticos y otras solicitudes innecesarias de terceros.

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 URLs contengan palabras clave específicas

Patrones comunes de URL intercambiables

Patrón de URLDescripciónEjemploRecomendación
Servicios publicitariosDominios de redes publicitariasad.doubleclick.net, googleadservices.com⭐ Seguro
Servicios analíticosScripts de estadísticas y análisisgoogle-analytics.com, hotjar.com⭐ Seguro
Plugins de redes socialesBotones de compartición en redes sociales, etc.platform.twitter.com, connect.facebook.net⭐ Seguro
Píxeles de seguimientoPíxeles que rastrean el comportamiento del usuarioURLs que contienen pixel, beacon, tracker⭐ Seguro
Archivos multimedia grandesArchivos grandes de video y audioExtensiones como .mp4, .webm, .mp3⭐ Seguro
Servicios de fuentesServicios en línea de fuentes tipográficasfonts.googleapis.com, use.typekit.net⭐ Seguro
Recursos CDNCDN de recursos estáticoscdn.jsdelivr.net, unpkg.com⚠️ Precaución

Ejemplo de código de interceptación de URL

import puppeteer from 'puppeteer-core';
import { Scrapeless } from '@scrapeless-ai/sdk';
import puppeteer from 'puppeteer-core';
 
const client = new Scrapeless({ apiKey: 'API Key' });
 
const { browserWSEndpoint } = client.browser.create({
    sessionName: 'sdk_test',
    sessionTTL: 180,
    proxyCountry: 'ANY',
    sessionRecording: true,
    fingerprint,
});
 
async function scrapeWithUrlBlocking(url) {
    const browser = await puppeteer.connect({
        browserWSEndpoint,
        defaultViewport: null
    });
    const page = await browser.newPage();
 
    // Enable request interception
    await page.setRequestInterception(true);
 
    // Define domains and URL patterns to block
    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/',
    ];
 
    // Intercept requests
    page.on('request', (request) => {
        const url = request.url();
 
        // Check domain
        if (BLOCKED_DOMAINS.some(domain => url.includes(domain))) {
            request.abort();
            console.log(`Blocked domain: ${url.substring(0, 50)}...`);
            return;
        }
 
        // Check path
        if (BLOCKED_PATHS.some(path => url.includes(path))) {
            request.abort();
            console.log(`Blocked path: ${url.substring(0, 50)}...`);
            return;
        }
 
        // Allow other requests
        request.continue();
    });
 
    await page.goto(url, {waitUntil: 'domcontentloaded'});
 
    // Extract data
    const data = await page.evaluate(() => {
        return {
            title: document.title,
            content: document.body.innerText.substring(0, 1000)
        };
    });
 
    await browser.close();
    return data;
}
 
// Usage
scrapeWithUrlBlocking('https://www.scrapeless.com')
    .then(data => console.log('Scraping result:', data))
    .catch(error => console.error('Scraping failed:', error));

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

Simular dispositivos móviles es otra estrategia efectiva de optimización de tráfico porque los sitios web móviles suelen ofrecer contenido más ligero.

Ventajas de la simulación de dispositivos móviles

  1. Versiones de páginas más ligeras: Muchos sitios web ofrecen contenido más conciso para dispositivos móviles
  2. Recursos de imágenes más pequeños: Las versiones móviles suelen cargar imágenes de menor tamaño
  3. CSS y JavaScript simplificados: Las versiones móviles suelen utilizar estilos y scripts simplificados
  4. Menos anuncios y contenido no esencial: Las versiones móviles a menudo eliminan ciertas funcionalidades no esenciales
  5. Respuesta adaptable: Obtener diseños de contenido optimizados para pantallas pequeñas

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

A continuación se muestran los parámetros de configuración para varios dispositivos móviles comúnmente utilizados:

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

O utilizar 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 from 'puppeteer-core';
import { Scrapeless } from '@scrapeless-ai/sdk';
 
const client = new Scrapeless({ apiKey: 'API Key' });
 
const { browserWSEndpoint } = client.browser.create({
    sessionName: 'sdk_test',
    sessionTTL: 180,
    proxyCountry: 'ANY',
    sessionRecording: true,
    fingerprint,
});
 
async function scrapeWithMobileEmulation(url) {
    const browser = await puppeteer.connect({
        browserWSEndpoint,
        defaultViewport: null
    });
 
    const page = await browser.newPage();
 
    // Set mobile device simulation
    const iPhone = KnownDevices['iPhone 15 Pro'];
    await page.emulate(iPhone);
 
    await page.goto(url, {waitUntil: 'domcontentloaded'});
    // Extract data
    const data = await page.evaluate(() => {
        return {
            title: document.title,
            content: document.body.innerText.substring(0, 1000)
        };
    });
 
    await browser.close();
    return data;
}
 
// Usage
scrapeWithMobileEmulation('https://www.scrapeless.com')
    .then(data => console.log('Scraping result:', data))
    .catch(error => console.error('Scraping failed:', error));

Ejemplo de optimización integral

A continuación se muestra un ejemplo integral que combina todos los esquemas de optimización:

import puppeteer, {KnownDevices} from 'puppeteer-core';
import { Scrapeless } from '@scrapeless-ai/sdk';
 
const client = new Scrapeless({ apiKey: 'API Key' });
 
const { browserWSEndpoint } = client.browser.create({
    sessionName: 'sdk_test',
    sessionTTL: 180,
    proxyCountry: 'ANY',
    sessionRecording: true,
    fingerprint,
});
 
async function optimizedScraping(url) {
    console.log(`Starting optimized scraping: ${url}`);
 
    // Record traffic usage
    let totalBytesUsed = 0;
 
    const browser = await puppeteer.connect({
        browserWSEndpoint,
        defaultViewport: null
    });
 
    const page = await browser.newPage();
 
    // Set mobile device simulation
    const iPhone = KnownDevices['iPhone 15 Pro'];
    await page.emulate(iPhone);
 
    // Set request interception
    await page.setRequestInterception(true);
 
    // Define resource types to block
    const BLOCKED_TYPES = [
        'image',
        'media',
        'font'
    ];
 
    // Define domains to block
    const BLOCKED_DOMAINS = [
        'google-analytics.com',
        'googletagmanager.com',
        'facebook.net',
        'doubleclick.net',
        'adservice.google.com'
    ];
 
    // Define URL paths to block
    const BLOCKED_PATHS = [
        '/ads/',
        '/analytics/',
        '/tracking/'
    ];
 
    // Intercept requests
    page.on('request', (request) => {
        const url = request.url();
        const resourceType = request.resourceType();
 
        // Check resource type
        if (BLOCKED_TYPES.includes(resourceType)) {
            console.log(`Blocked resource type: ${resourceType} - ${url.substring(0, 50)}...`);
            request.abort();
            return;
        }
 
        // Check domain
        if (BLOCKED_DOMAINS.some(domain => url.includes(domain))) {
            console.log(`Blocked domain: ${url.substring(0, 50)}...`);
            request.abort();
            return;
        }
 
        // Check path
        if (BLOCKED_PATHS.some(path => url.includes(path))) {
            console.log(`Blocked path: ${url.substring(0, 50)}...`);
            request.abort();
            return;
        }
 
        // Allow other requests
        request.continue();
    });
 
    // Monitor network traffic
    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'});
 
    // Simulate scrolling to trigger lazy-loading content
    await page.evaluate(() => {
        window.scrollBy(0, window.innerHeight);
    });
 
    await new Promise(resolve => setTimeout(resolve, 1000))
 
    // Extract data
    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
            }))
        };
    });
 
    // Output traffic usage statistics
    console.log(`\nTraffic Usage Statistics:`);
    console.log(`Used: ${(totalBytesUsed / 1024 / 1024).toFixed(2)} MB`);
 
    await browser.close();
    return data;
}
 
// Usage
optimizedScraping('https://www.scrapeless.com')
    .then(data => console.log('Scraping complete:', data))
    .catch(error => console.error('Scraping failed:', 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. A continuación se muestra el código de ejemplo sin optimizar:

import puppeteer from 'puppeteer-core';
import { Scrapeless } from '@scrapeless-ai/sdk';
 
const client = new Scrapeless({ apiKey: 'API Key' });
 
const { browserWSEndpoint } = client.browser.create({
    sessionName: 'sdk_test',
    sessionTTL: 180,
    proxyCountry: 'ANY',
    sessionRecording: true,
    fingerprint,
});
 
async function unoptimizedScraping(url) {
  console.log(`Starting unoptimized scraping: ${url}`);
 
  // Record traffic usage
  let totalBytesUsed = 0;
 
  const browser = await puppeteer.connect({
    browserWSEndpoint,
    defaultViewport: null
  });
 
  const page = await browser.newPage();
 
  // Set request interception
  await page.setRequestInterception(true);
 
  // Intercept requests
  page.on('request', (request) => {
    request.continue();
  });
 
  // Monitor network traffic
  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'});
 
  // Simulate scrolling to trigger lazy-loading content
  await page.evaluate(() => {
    window.scrollBy(0, window.innerHeight);
  });
 
  await new Promise(resolve => setTimeout(resolve, 1000))
 
  // Extract data
  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
      }))
    };
  });
 
  // Output traffic usage statistics
  console.log(`\nTraffic Usage Statistics:`);
  console.log(`Used: ${(totalBytesUsed / 1024 / 1024).toFixed(2)} MB`);
 
  await browser.close();
  return data;
}
 
// Usage
unoptimizedScraping('https://www.scrapeless.com')
  .then(data => console.log('Scraping complete:', data))
  .catch(error => console.error('Scraping failed:', error));

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

EscenarioTráfico utilizado (MB)Relación de ahorro
No optimizado6.03—
Optimizado0.81≈ 86.6 %

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