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:
- Intercepción de recursos: Reducir el consumo de tráfico interceptando las solicitudes de recursos innecesarias.
- 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: Usar configuraciones de dispositivos móviles para obtener versiones de páginas más ligeras.
- 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 recurso | Descripción | Ejemplo | Impacto después de la intercepción | Recomendación |
---|---|---|---|---|
image | Recursos de imagen | Imágenes JPG/PNG/GIF/WebP | Las imágenes no se mostrarán | ⭐ Seguro |
font | Archivos de fuente | Fuentes TTF/WOFF/WOFF2 | Se usarán las fuentes predeterminadas del sistema | ⭐ Seguro |
media | Archivos multimedia | Archivos de vídeo/audio | No se puede reproducir el contenido multimedia | ⭐ Seguro |
manifest | Manifiesto de aplicación web | Archivo de configuración PWA | La funcionalidad PWA puede verse afectada | ⭐ Seguro |
prefetch | Recursos prefetch | <link rel="prefetch"> | Impacto mínimo en la página | ⭐ Seguro |
stylesheet | Hoja de estilo CSS | Archivos CSS externos | Se pierden los estilos de la página, puede afectar el diseño | ⚠️ Precaución |
websocket | WebSocket | Conexión de comunicación en tiempo real | Funcionalidad en tiempo real deshabilitada | ⚠️ Precaución |
eventsource | Eventos enviados por el servidor | Datos push del servidor | Funcionalidad push deshabilitada | ⚠️ Precaución |
preflight | Solicitud de preflight CORS | Solicitud OPTIONS | Fallan las solicitudes de origen cruzado | ⚠️ Precaución |
script | Scripts JavaScript | Archivos JS externos | Funcionalidad dinámica deshabilitada, la SPA puede no renderizar | ❌ Evitar |
xhr | Solicitudes XHR | Solicitudes de datos AJAX | No se pueden obtener datos dinámicos | ❌ Evitar |
fetch | Solicitudes Fetch | Solicitudes AJAX modernas | No se pueden obtener datos dinámicos | ❌ Evitar |
document | Documento principal | Pá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
- 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 URL contengan palabras clave específicas
Patrones de URL interceptables comunes
Patrón de URL | Descripción | Ejemplo | Recomendación |
---|---|---|---|
Servicios de publicidad | Dominios de red publicitaria | ad.doubleclick.net , googleadservices.com | ⭐ Seguro |
Servicios de análisis | Scripts de estadísticas y análisis | google-analytics.com , hotjar.com | ⭐ Seguro |
Complementos de redes sociales | Botones para compartir en redes sociales, etc. | platform.twitter.com , connect.facebook.net | ⭐ Seguro |
Pixels de seguimiento | Pixels que rastrean el comportamiento del usuario | URL que contienen pixel , beacon , tracker | ⭐ Seguro |
Archivos multimedia grandes | Archivos de vídeo y audio grandes | Extensiones como .mp4 , .webm , .mp3 | ⭐ Seguro |
Servicios de fuentes | Servicios de fuentes online | 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 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
- Versiones de página más ligeras: Muchos sitios web proporcionan un contenido más conciso para los dispositivos móviles
- Recursos de imagen más pequeños: Las versiones móviles suelen cargar imágenes más pequeñas
- CSS y JavaScript simplificados: Las versiones móviles suelen utilizar estilos y scripts simplificados
- Anuncios y contenido no esencial reducidos: Las versiones móviles a menudo eliminan algunas funcionalidades no esenciales
- 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:
Escenario | Tráfico usado (MB) | Ratio de ahorro |
---|---|---|
Sin optimizar | 6.03 | — |
Optimizado | 0.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.