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:
- Intercepción de recursos: Reducir el consumo de tráfico interceptando solicitudes de recursos innecesarios.
- 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.
- Simular dispositivos móviles: Utilizar configuraciones de dispositivos móviles para obtener versiones más ligeras de las páginas.
- 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 recurso | Descripción | Ejemplo | Impacto tras la interceptación | Recomendación |
|---|---|---|---|---|
image | Recursos de imagen | Imágenes JPG/PNG/GIF/WebP | Las imágenes no se mostrarán | ⭐ Seguro |
font | Archivos de fuentes | Fuentes TTF/WOFF/WOFF2 | Se utilizarán las fuentes predeterminadas del sistema | ⭐ Seguro |
media | Archivos multimedia | Archivos de video/audio | El contenido multimedia no se podrá reproducir | ⭐ Seguro |
manifest | Web App Manifest | Archivo de configuración PWA | La funcionalidad PWA podría verse afectada | ⭐ Seguro |
prefetch | Recursos de prefijado | <link rel="prefetch"> | Impacto mínimo en la página | ⭐ Seguro |
stylesheet | Hoja de estilos CSS | Archivos CSS externos | Se pierden los estilos de la página, podría afectar al diseño | ⚠️ Precaución |
websocket | WebSocket | Conexión de comunicación en tiempo real | La funcionalidad en tiempo real queda deshabilitada | ⚠️ Precaución |
eventsource | Server-Sent Events | Datos enviados por el servidor | La funcionalidad de envío queda deshabilitada | ⚠️ Precaución |
preflight | Solicitud previa CORS | Solicitud OPTIONS | Las solicitudes de origen cruzado fallan | ⚠️ Precaución |
script | Scripts de JavaScript | Archivos JS externos | Funcionalidad dinámica deshabilitada, es posible que las SPA no se rendericen | ❌ Evitar |
xhr | Solicitudes XHR | Solicitudes de datos AJAX | Imposible obtener datos dinámicos | ❌ Evitar |
fetch | Solicitudes fetch | Solicitudes AJAX modernas | Imposible obtener datos dinámicos | ❌ Evitar |
document | Documento principal | La propia página HTML | La 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
- Interceptar por dominio: Bloquear todas las solicitudes de un dominio específico
- Interceptar por ruta: Bloquear solicitudes de una ruta específica
- Interceptar por tipo de archivo: Bloquear archivos con extensiones específicas
- Interceptar por palabra clave: Bloquear solicitudes cuyas URLs contengan palabras clave específicas
Patrones comunes de URL intercambiables
| Patrón de URL | Descripción | Ejemplo | Recomendación |
|---|---|---|---|
| Servicios publicitarios | Dominios de redes publicitarias | ad.doubleclick.net, googleadservices.com | ⭐ Seguro |
| Servicios analíticos | Scripts de estadísticas y análisis | google-analytics.com, hotjar.com | ⭐ Seguro |
| Plugins de redes sociales | Botones de compartición en redes sociales, etc. | platform.twitter.com, connect.facebook.net | ⭐ Seguro |
| Píxeles de seguimiento | Píxeles que rastrean el comportamiento del usuario | URLs que contienen pixel, beacon, tracker | ⭐ Seguro |
| Archivos multimedia grandes | Archivos grandes de video y audio | Extensiones como .mp4, .webm, .mp3 | ⭐ Seguro |
| Servicios de fuentes | Servicios en línea de fuentes tipográficas | fonts.googleapis.com, use.typekit.net | ⭐ Seguro |
| Recursos CDN | CDN de recursos estáticos | cdn.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
- Versiones de páginas más ligeras: Muchos sitios web ofrecen contenido más conciso para dispositivos móviles
- Recursos de imágenes más pequeños: Las versiones móviles suelen cargar imágenes de menor tamaño
- CSS y JavaScript simplificados: Las versiones móviles suelen utilizar estilos y scripts simplificados
- Menos anuncios y contenido no esencial: Las versiones móviles a menudo eliminan ciertas funcionalidades no esenciales
- 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:
| Escenario | Tráfico utilizado (MB) | Relación de ahorro |
|---|---|---|
| No optimizado | 6.03 | — |
| Optimizado | 0.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.