Cuando pagas el tráfico de proxy por gigabyte, cada byte de una página cuesta lo mismo: el párrafo que buscabas, el menú que lo rodea, el script de analítica, la imagen principal y el archivo de la fuente. La mayoría de los equipos saben que las páginas pesan mucho. Pocos saben qué parte de lo que descargan es realmente el dato que conservan.
Lo hemos medido. El 8 de octubre de 2026 descargamos las páginas de inicio de los 1.000 dominios principales del ranking Tranco, seguimos un enlace de tipo artículo desde cada una, y medimos adónde van los bytes: en el cable, en el HTML y en todo lo que descarga un navegador. Este estudio comparte los resultados, el código que usamos y lo que significan para los presupuestos de scraping.
Conclusiones clave
- El contenido principal de una página típica es alrededor del 1% de su HTML. En la página de contenido mediana, 3,2 KB de texto principal se encontraban dentro de 260 KiB de HTML.
- La compresión hace la mayor parte del ahorro de forma gratuita. El mismo HTML llegó como 46 KiB en el cable, unas 5,4 veces más pequeño, por lo que el texto principal fue alrededor del 6% de los bytes realmente transferidos.
- Los scripts en línea son la mayor porción individual del HTML. En todas las páginas de contenido, el JavaScript en línea representó el 38% de los bytes del HTML, el CSS en línea el 15% y el SVG en línea el 10%. El texto principal fue el 1,3%.
- Un navegador multiplica la factura. La página de contenido mediana descargó 2,6 MB y 81 peticiones en un navegador headless. El propio documento HTML fue alrededor del 3% de eso.
- Bloquear imágenes, medios y fuentes reduce a la mitad el tráfico del navegador. En todas las páginas eliminó el 48% de los bytes; en la página mediana, una carga ligera fue el 71% de una carga completa.
Cómo lo medimos
- Muestra. Los 1.000 dominios principales de la lista Tranco (lista Q2K34). Muchos son hosts de API, CDN y seguimiento sin sitio web, por lo que 431 sitios distintos devolvieron una página de inicio utilizable. Desde cada página de inicio seguimos el primer enlace del mismo sitio que parecía una página de contenido (una ruta que termina en un slug de cuatro o más palabras, excluyendo páginas de inicio de sesión, legales y de ayuda), lo que dio 278 páginas de contenido utilizables.
- Capa HTML. Una petición por página con un User-Agent de navegador de escritorio habitual y compresión activada. Registramos los bytes recibidos, los descomprimimos y dividimos el HTML en scripts en línea, estilos en línea (incluyendo atributos
style), SVG en línea y JSON-LD. Extrajimos el contenido principal con trafilatura, una biblioteca de extracción de código abierto, y contamos sus bytes como texto normalizado. - Capa de navegador. Cargamos cada página de contenido en Chromium headless, esperamos al evento load más tres segundos, y sumamos los bytes transferidos de cada respuesta por tipo de recurso. Luego la cargamos de nuevo con imágenes, medios y fuentes bloqueados.
- Exclusiones. Se excluyeron antes del análisis las respuestas de error, las respuestas no HTML y las páginas de bloqueo (páginas de desafío, páginas de “acceso denegado” y respuestas casi vacías), de modo que los resultados describen páginas que realmente sirvieron contenido.
La capa HTML
| Medida (mediana por página) | Páginas de contenido | Páginas de inicio |
|---|---|---|
| Páginas medidas | 278 | 431 |
| Bytes en el cable | 46 KiB | 51 KiB |
| HTML tras descompresión | 260 KiB | 270 KiB |
| Texto de contenido principal | 3,2 KB | 1,4 KB |
| Contenido principal como parte del HTML | 1,2% | 0,6% |
| Contenido principal como parte de los bytes transferidos | 6,2% | 3,1% |
| Todo el texto visible como parte del HTML | 3,6% | 2,4% |
Las páginas de contenido llevan más texto que las páginas de inicio, como cabía esperar, pero la forma general es la misma: el texto que un scraper conserva es una fracción mínima del documento que descarga. Incluso contando cada palabra visible de la página, incluyendo menús, pies de página y avisos de cookies, el texto fue menos del 4% del HTML.
¿Adónde va el resto? En las 278 páginas de contenido:
| Parte del HTML | Parte de los bytes del HTML |
|---|---|
| JavaScript en línea | 37,5% |
| CSS en línea y atributos style | 15,4% |
| SVG en línea | 9,8% |
| Datos estructurados JSON-LD | 0,9% |
| Texto de contenido principal | 1,3% |
| Marcado, atributos y todo lo demás | 35,1% |
El JavaScript en línea es el bloque individual más grande: estado de frameworks, configuración y código de seguimiento incrustado directamente en la página. Los sitios modernos a menudo envían todos los datos de la página como un blob JSON dentro de una etiqueta script y los renderizan en el cliente, por lo que los scripts superan en peso al texto que acaban mostrando.
Eso también es una oportunidad. El JSON-LD de estas páginas promedió menos del 1% del HTML, y cuando una página incrusta sus datos como JSON, ese blob suele ser mucho más limpio de analizar que el marcado renderizado. Nuestras guías sobre extraer JSON-LD en lugar de analizar HTML y encontrar la API detrás de la página cubren ambas cosas.
La compresión importa más que cualquier otra cosa en esta capa. La página mediana se redujo 5,4 veces durante la transferencia. Solo 10 de las 278 páginas de contenido se sirvieron sin comprimir, pero un scraper que no solicita compresión obtiene los 260 KiB completos cada vez. Si tu cliente HTTP envía Accept-Encoding: gzip, deflate, br, ya estás pagando por 46 KiB, no por 260.
La capa del navegador
Renderizar una página en un navegador descarga todo lo que la página solicita, no solo el documento.
| Medida (por página de contenido) | Valor |
|---|---|
| Páginas medidas en el navegador | 274 |
| Bytes transferidos mediana, carga completa | 2,6 MB |
| Mitad central de las páginas | 1,2 a 4,3 MB |
| Peticiones mediana, carga completa | 81 |
| Documento HTML como parte de la carga completa (mediana) | 3% |
| Texto de contenido principal como parte de la carga completa (mediana) | 0,13% |
Por tipo de recurso, en todas las cargas completas:
| Tipo de recurso | Parte de los bytes |
|---|---|
| Imágenes | 38,1% |
| Scripts | 35,9% |
| Medios (vídeo y audio) | 7,4% |
| Fuentes | 6,6% |
| Hojas de estilo | 2,8% |
| Documentos HTML | 2,4% |
| Llamadas a la API (fetch y XHR) | 4,9% |
| Otros | 1,9% |
Bloquear imágenes, medios y fuentes, que un scraper casi nunca necesita, redujo la página mediana a 1,5 MB y 55 peticiones. En todas las páginas, la carga bloqueada transfirió el 52% de los bytes de la completa. Los scripts son más difíciles de bloquear de forma segura, porque la página a menudo los necesita para renderizar el contenido que buscabas.
Qué significa esto para un presupuesto de scraping
Tomemos un trabajo que recoge un millón de páginas de contenido al mes y conserva solo su texto principal. Usando las medianas anteriores:
| Cómo se obtienen las páginas | Tráfico por millón de páginas |
|---|---|
| Solo el texto principal, si pudieras obtener únicamente eso | unos 3,2 GB |
| Solo HTML, comprimido | unos 47 GB |
| Solo HTML, sin comprimir | unos 265 GB |
| Navegador, imágenes, medios y fuentes bloqueados | unos 1,5 TB |
| Navegador, carga completa | unos 2,6 TB |
La diferencia entre la primera fila y la última es de aproximadamente 800 veces. El orden práctico de los ahorros se deriva de ello:
- Obtén el HTML sin un navegador siempre que el dato esté en el HTML. Eso por sí solo marca la diferencia entre gigabytes y terabytes.
- Solicita siempre compresión. Reduce el tráfico de HTML unas cinco veces sin coste alguno.
- Cuando debas renderizar, bloquea imágenes, medios y fuentes. Reduce aproximadamente a la mitad el tráfico del navegador.
- Busca una fuente más pequeña del mismo dato: JSON-LD, un blob JSON incrustado o la API a la que la propia página llama.
Nuestra guía sobre reducir los costes de ancho de banda de proxy cubre cada una de estas técnicas en la práctica, y estimar tu ancho de banda mensual muestra cómo convertir el peso de las páginas en un plan.
Mide tus propias páginas
Las medianas de los principales sitios son un punto de partida; lo que importa son tus objetivos. La función siguiente obtiene una página e informa de adónde van sus bytes, usando el mismo método que el estudio:
import gzip
import re
import zlib
import brotli
import requests
import trafilatura
from lxml import html as lxml_html
def decode(raw, content_encoding):
"""Undo Content-Encoding by hand, so we can count the compressed bytes first."""
for coding in reversed([c.strip() for c in content_encoding.lower().split(",") if c.strip()]):
if coding == "gzip":
raw = gzip.decompress(raw)
elif coding == "br":
raw = brotli.decompress(raw)
elif coding == "deflate":
raw = zlib.decompress(raw)
return raw
def size(text):
return len(text.encode("utf-8"))
def payload_breakdown(url, session=None):
"""Where the bytes of one HTML page go, from the wire down to the main content."""
session = session or requests.Session()
response = session.get(url, timeout=30, stream=True,
headers={"Accept-Encoding": "gzip, deflate, br"})
wire = response.raw.read(decode_content=False)
page = decode(wire, response.headers.get("Content-Encoding", "")).decode(
response.encoding or "utf-8", errors="replace")
doc = lxml_html.document_fromstring(page)
scripts = doc.xpath("//script")
json_ld = sum(size(s.text or "") for s in scripts if s.get("type") == "application/ld+json")
inline_js = sum(size(s.text or "") for s in scripts
if not s.get("src") and s.get("type") != "application/ld+json")
inline_css = sum(size(s.text or "") for s in doc.xpath("//style")) + sum(size(v) for v in doc.xpath("//@style"))
inline_svg = sum(size(lxml_html.tostring(s, encoding="unicode"))
for s in doc.xpath("//*[local-name()='svg'][not(ancestor::*[local-name()='svg'])]"))
main_text = trafilatura.extract(page, include_tables=True) or ""
return {
"status": response.status_code,
"wire_bytes": len(wire),
"html_bytes": size(page),
"inline_js": inline_js,
"inline_css": inline_css,
"inline_svg": inline_svg,
"json_ld": json_ld,
"main_text": size(re.sub(r"\s+", " ", main_text).strip()),
}
Lee el cuerpo de la respuesta antes de la descompresión, de modo que wire_bytes es lo que realmente transferiste, y luego lo decodifica manualmente. Ejecútalo en algunas páginas de cada uno de tus objetivos:
from payload import payload_breakdown
import requests
session = requests.Session()
session.headers["User-Agent"] = "ExampleStudy/1.0 (+https://example.com/bot)"
b = payload_breakdown("https://en.wikipedia.org/wiki/Web_scraping", session)
print(b)
print(f"main content: {b['main_text'] / b['html_bytes']:.1%} of the HTML, "
f"{b['main_text'] / b['wire_bytes']:.1%} of the bytes transferred")
{'status': 200, 'wire_bytes': 46860, 'html_bytes': 236286, 'inline_js': 6964, 'inline_css': 6303, 'inline_svg': 0, 'json_ld': 640, 'main_text': 26979}
main content: 11.4% of the HTML, 57.6% of the bytes transferred
Wikipedia es una página eficiente según estos estándares: su texto principal supera el 11% del HTML, casi diez veces la mediana que medimos. Tus objetivos se situarán en algún punto de ese rango, y saber dónde te indica si la recopilación solo de HTML, la compresión o una fuente de datos diferente ahorrará más.
Límites de la medición
- Solo los principales sitios. Los 1.000 dominios principales son sitios grandes y bien diseñados. Los sitios más pequeños pueden ser más ligeros o mucho más pesados.
- Una página de contenido por sitio. Seguimos el primer enlace de tipo artículo de cada página de inicio. Las páginas de producto, resultados de búsqueda y páginas de listado pueden diferir.
- El contenido principal es una estimación. Las bibliotecas de extracción pueden omitir o incluir en exceso contenido, en particular en páginas que son sobre todo navegación. Trata las cifras de texto principal como aproximadas; las mediciones de bytes son exactas.
- Una instantánea, una red. Las páginas se obtuvieron una vez, el 8 de octubre de 2026, desde una única red en Europa. Los sitios sirven páginas, anuncios y medios diferentes según la ubicación y a lo largo del tiempo.
- Las cargas del navegador tenían un límite. Dejamos de medir tres segundos después del evento load. Las páginas que siguen cargando contenido después transferirían más de lo que registramos.
Preguntas frecuentes
¿Qué parte de una página web es el contenido real?
En la página de contenido mediana entre los 1.000 sitios principales, el texto principal fue alrededor del 1,2% del HTML y alrededor del 6% de los bytes comprimidos transferidos. En una carga completa en navegador, fue alrededor del 0,13% de los bytes.
¿Bloquear imágenes reduce el ancho de banda de proxy?
Sí. Bloquear imágenes, medios y fuentes en un navegador headless eliminó el 48% de los bytes en nuestra muestra. Las imágenes por sí solas fueron el 38% del tráfico del navegador.
¿Es más barato hacer scraping sin un navegador?
Normalmente, por un margen amplio. La página de contenido mediana fue de 46 KiB como HTML comprimido y 2,6 MB en una carga completa en navegador, una diferencia de más de 50 veces.
¿Debería solicitar respuestas comprimidas al hacer scraping?
Sí. La página mediana fue 5,4 veces más pequeña comprimida. La mayoría de los clientes HTTP solicitan compresión por defecto, pero compruébalo, porque un cliente que no lo haga paga el tamaño completo de cada página.
La conclusión
El dato que un scraper conserva es una fracción minúscula de lo que descarga: alrededor del 1% del HTML de una página típica y una fracción de un punto porcentual de lo que descarga un navegador. La mayor parte de esa sobrecarga es evitable. Obtén el HTML en lugar de renderizar cuando puedas, mantén la compresión activada, bloquea los recursos pesados cuando debas renderizar, y busca datos estructurados o APIs que lleven la misma información en muchos menos bytes.
Fuentes y referencias
- Tranco, lista Q2K34, el ranking de sitios principales orientado a la investigación utilizado para la muestra.
- Barbaresi, A. (2021). Trafilatura: A Web Scraping Library and Command-Line Tool for Text Discovery and Extraction. ACL 2021 System Demonstrations.
- Páginas medidas por Shifter el 8 de octubre de 2026 usando el código anterior y Chromium headless.