Integración7 min de lectura

Bloquear imágenes, fuentes y medios en Playwright: menos tráfico

Bloquea imágenes, fuentes y medios en Playwright con route() en Python y Node.js, en todo el contexto o por host, con las salvedades de la caché y los service workers.

En esta página

El punto de partida

Aborta las peticiones pesadas que un trabajo de scraping no necesita, en todas las páginas de un contexto de navegador, y luego mide si la ejecución realmente movió menos bytes.

Mide el trabajo antes de bloquear nada

Las imágenes, las fuentes y el vídeo pueden suponer gran parte de lo que descarga una página, y muchos trabajos de scraping nunca los leen. Bloquearlos mediante la interceptación de peticiones es una forma habitual de reducir el tráfico del proxy. Que ayude a tu trabajo depende de tus páginas, así que empieza por una medición: ejecuta el trabajo una vez tal como está y registra adónde van los bytes. scrapescope es un medidor local, gratuito y de código abierto, que informa de los bytes por host y por tipo de recurso de una ejecución de Playwright y modela el efecto de bloquear imágenes, medios y fuentes.

Los ejemplos siguientes usan Playwright 1.63 y la dirección de ejemplo https://example.com/. Apunta TARGET_URL a una página que tengas permiso para automatizar y mantén la configuración y las credenciales del proxy en tus propias opciones de lanzamiento o de contexto; no hacen falta para probar el bloqueo en sí.

Bloquear por tipo de recurso en Python

Playwright etiqueta cada petición con el tipo de recurso que percibió el motor de renderizado: document, stylesheet, image, media, font, script, texttrack, xhr, fetch, eventsource, websocket, manifest u other. Registra una sola ruta para todas las URL en el contexto del navegador y aborta los tres tipos pesados; el resto de las peticiones continúa sin cambios.

Guárdalo como block_resources.py y ejecuta python block_resources.py después de pip install playwright y playwright install chromium. El contexto también bloquea los service workers, por el motivo que se explica más abajo.

block_resources.py · Playwright para Python

python
import os

from playwright.sync_api import Route, sync_playwright

TARGET_URL = os.environ.get("TARGET_URL", "https://example.com/")
BLOCKED_TYPES = {"image", "media", "font"}


def block_heavy(route: Route) -> None:
    if route.request.resource_type in BLOCKED_TYPES:
        route.abort()
    else:
        route.continue_()


with sync_playwright() as p:
    browser = p.chromium.launch()
    try:
        context = browser.new_context(service_workers="block")
        context.route("**/*", block_heavy)
        page = context.new_page()
        response = page.goto(TARGET_URL, wait_until="load")
        print({"status": response.status if response else None})
        context.close()
    finally:
        browser.close()

Bloquear por tipo de recurso en Node.js

La versión para Node.js es la misma ruta con los mismos tres tipos. route.request().resourceType() devuelve el tipo, route.abort() hace fallar la petición antes de que salga del navegador y route.continue() envía todo lo demás tal como estaba.

Guárdalo como block-resources.mjs y ejecuta node block-resources.mjs después de npm install playwright y npx playwright install chromium.

block-resources.mjs · Playwright Chromium

javascript
import { chromium } from 'playwright';

const TARGET_URL = process.env.TARGET_URL ?? 'https://example.com/';
const BLOCKED_TYPES = new Set(['image', 'media', 'font']);

const browser = await chromium.launch();
try {
  const context = await browser.newContext({ serviceWorkers: 'block' });
  await context.route('**/*', route =>
    BLOCKED_TYPES.has(route.request().resourceType()) ? route.abort() : route.continue());
  const page = await context.newPage();
  const response = await page.goto(TARGET_URL, { waitUntil: 'load' });
  console.log({ status: response?.status() });
  await context.close();
} finally {
  await browser.close();
}

Aplícalo a todas las páginas del contexto del navegador

Una ruta en el contexto del navegador se aplica a las peticiones de cualquier página de ese contexto: las páginas que abras más tarde, las segundas pestañas y las ventanas emergentes que una página abre con window.open(). Una ruta en una sola página se aplica únicamente a esa página, de modo que una ventana emergente o una pestaña nueva volverían a cargar sus imágenes y fuentes. Registra la ruta del contexto antes de que cargue la primera página y ponla en una única función auxiliar para que todos los trabajos usen la misma regla.

Cuando una petición coincide con ambas, las rutas de página tienen prioridad sobre las rutas del contexto del navegador, y cuando coinciden varias rutas de página, gana la registrada más recientemente. Mantén una sola ruta de bloqueo por contexto para evitar sorpresas.

context-wide.mjs · una ruta para todas las páginas

javascript
import { chromium } from 'playwright';

const TARGET_URL = process.env.TARGET_URL ?? 'https://example.com/';
const BLOCKED_TYPES = new Set(['image', 'media', 'font']);

// One route on the context covers every page the context opens, including
// tabs opened later and popups. Register it before the first page loads.
async function blockHeavyResources(context) {
  await context.route('**/*', route =>
    BLOCKED_TYPES.has(route.request().resourceType()) ? route.abort() : route.continue());
}

const browser = await chromium.launch();
try {
  const context = await browser.newContext({ serviceWorkers: 'block' });
  await blockHeavyResources(context);
  const first = await context.newPage();
  const second = await context.newPage();
  await Promise.all([
    first.goto(TARGET_URL, { waitUntil: 'load' }),
    second.goto(TARGET_URL, { waitUntil: 'load' }),
  ]);
  console.log({ pages: context.pages().length });
  await context.close();
} finally {
  await browser.close();
}

Bloquear hosts pesados concretos por patrón de URL

A veces el problema es un solo host: una CDN de vídeo, un servidor de anuncios o un servicio de fuentes. route() también acepta un patrón de URL, así que puedes abortar todo lo que venga de esos hosts y dejar en paz los tipos de recurso. Un glob como https://media.example.net/** coincide con todas las URL de un host; una expresión regular puede enumerar varios. Los hosts de abajo son marcadores de posición; toma los nombres reales de tu medición y no de una lista de bloqueo genérica.

block_hosts.py · abortar hosts concretos

python
import os
import re

from playwright.sync_api import sync_playwright

TARGET_URL = os.environ.get("TARGET_URL", "https://example.com/")
# A glob covers every URL on one host; a regular expression can cover several.
HEAVY_HOST = "https://media.example.net/**"
HEAVY_HOSTS = re.compile(r"^https://(video\.example\.org|fonts\.example\.com)/")

with sync_playwright() as p:
    browser = p.chromium.launch()
    try:
        context = browser.new_context(service_workers="block")
        context.route(HEAVY_HOST, lambda route: route.abort())
        context.route(HEAVY_HOSTS, lambda route: route.abort())
        page = context.new_page()
        response = page.goto(TARGET_URL, wait_until="load")
        print({"status": response.status if response else None})
        context.close()
    finally:
        browser.close()

El enrutamiento desactiva la caché HTTP

La referencia de la API de Playwright indica, tanto para page.route() como para browserContext.route(): "Enabling routing disables http cache." (activar el enrutamiento desactiva la caché HTTP). En cuanto se registra cualquier ruta, las hojas de estilo, los scripts y otros recursos repetidos que el navegador normalmente reutilizaría desde su caché se descargan de nuevo en cada página. En un trabajo que carga muchas páginas en un mismo contexto, esa transferencia adicional puede compensar parte de lo que elimina el bloqueo, o todo. Por eso no basta con una prueba de una sola página: mide la ejecución completa.

Bloquea los service workers mientras interceptas

Las peticiones que gestiona el service worker de una página pueden saltarse tus rutas: la referencia de la API señala que browserContext.route() no intercepta las peticiones interceptadas por un Service Worker y recomienda desactivar los service workers al usar la interceptación de peticiones, estableciendo serviceWorkers en 'block'. Esa es la opción de contexto service_workers="block" (Python) y serviceWorkers: 'block' (Node.js) de todos los ejemplos anteriores. Un sitio que depende de un service worker, por ejemplo para almacenar contenido en caché y usarlo sin conexión, puede comportarse de otra manera cuando no puede registrarlo.

El bloqueo puede cambiar lo que hace la página

Bloquear imágenes, medios y fuentes puede romper la extracción, cambiar el comportamiento de una página o atraer el escrutinio de los sistemas antibot. Una página puede esperar una imagen antes de renderizar los datos que lees, cargar contenido de forma diferida solo tras eventos multimedia, o considerar inusual un navegador que nunca descarga imágenes. Comprueba que los valores que extraes no cambian tras activar la ruta y mantén el bloqueo dentro de lo que permiten las condiciones del sitio.

Vuelve a medir y compara

Ejecuta el mismo trabajo dos veces a través del mismo proveedor, una sin la ruta y otra con ella, y compara las dos ejecuciones. Una segunda ejecución medida es la prueba; un modelo o una estimación es una predicción. scrapescope puede comparar dos informes en paralelo en su visor, incluidos los bytes por host y por tipo de recurso, para que veas qué eliminó la ruta y qué volvió a añadir la caché HTTP perdida.

Antes de pasar a producción

  • Mide el trabajo una vez sin bloqueo antes de cambiar nada.
  • Registra la ruta en el contexto del navegador, antes de que cargue la primera página.
  • Bloquea los service workers en las opciones del contexto mientras haya rutas activas.
  • Confirma que los valores extraídos no cambian después de bloquear.
  • Compara una segunda ejecución medida a través del mismo proveedor antes de confiar en el cambio.

Fuentes y lecturas adicionales

Referencias técnicas usadas para esta guía. Consulta la documentación de tu versión instalada y la configuración compatible de tu proveedor.