当你按字节数为代理流量付费时,页面中的每个字节成本都是一样的:你想要的那段正文、周围的菜单、分析脚本、主图和字体文件,概莫能外。大多数团队都知道页面很”重”,但很少有人知道下载的内容中有多少是自己真正会保留的数据。
我们对此做了测量。2026年10月8日,我们抓取了Tranco排名前1,000个域名的首页,从每个首页追踪了一个文章类型的链接,并测量了字节的去向:在传输线路上、在HTML中、以及浏览器下载的一切内容中。本研究分享了结果、我们使用的代码,以及这些数据对抓取预算意味着什么。
关键结论
- 一个典型页面的主体内容大约只占其HTML的1%。 在中位数内容页面上,3.2 KB的正文文字被包含在260 KiB的HTML中。
- 压缩几乎免费地完成了大部分节省工作。 同样的HTML在传输线路上只有46 KiB,大约缩小了5.4倍,因此主体文字约占实际传输字节的6%。
- 内联脚本是HTML中占比最大的单项。 在所有内容页面中,内联JavaScript占HTML字节的38%,内联CSS占15%,内联SVG占10%。主体文字占1.3%。
- 浏览器会使账单倍增。 中位数内容页面在无头浏览器中拉取了2.6 MB数据和81个请求。HTML文档本身仅占其中约3%。
- 屏蔽图片、媒体和字体可将浏览器流量减半。 在所有页面中,这样做可减少48%的字节;在中位数页面上,精简加载只有完整加载的71%。
我们如何测量
- 样本。 Tranco列表(列表Q2K34)的前1,000个域名。其中许多是API、CDN和追踪主机,没有网站,因此只有431个独立站点返回了可用的首页。从每个首页,我们追踪了第一个看起来像内容页面的同站链接(路径以四个或更多单词组成的slug结尾,排除登录、法律和帮助页面),最终得到278个可用的内容页面。
- HTML层。 对每个页面发送一次请求,使用常规桌面浏览器User-Agent并启用压缩。我们记录了接收到的字节数,对其解压,并将HTML拆分为内联脚本、内联样式(包括
style属性)、内联SVG和JSON-LD。我们使用开源提取库trafilatura提取了主体内容,并将其字节数计为规范化文本。 - 浏览器层。 我们在无头Chromium中加载每个内容页面,等待load事件加上三秒钟,并按资源类型汇总每个响应传输的字节数。然后我们再次加载该页面,但屏蔽了图片、媒体和字体。
- 排除项。 错误响应、非HTML响应和拦截页面(验证页面、“拒绝访问”页面和几乎空白的响应)在分析前被排除,因此结果描述的是实际提供了内容的页面。
HTML层
| 衡量指标(每页中位数) | 内容页面 | 首页 |
|---|---|---|
| 测量的页面数 | 278 | 431 |
| 传输线路上的字节数 | 46 KiB | 51 KiB |
| 解压后的HTML | 260 KiB | 270 KiB |
| 主体内容文字 | 3.2 KB | 1.4 KB |
| 主体内容占HTML的比例 | 1.2% | 0.6% |
| 主体内容占传输字节的比例 | 6.2% | 3.1% |
| 所有可见文字占HTML的比例 | 3.6% | 2.4% |
正如预期,内容页面比首页携带更多文字,但整体形态是一致的:抓取工具保留的文字只是其下载文档中的一小部分。即使计算页面上的每一个可见字词,包括菜单、页脚和cookie通知,文字也不到HTML的4%。
其余部分去了哪里?在全部278个内容页面中:
| HTML的组成部分 | 占HTML字节的比例 |
|---|---|
| 内联JavaScript | 37.5% |
| 内联CSS和style属性 | 15.4% |
| 内联SVG | 9.8% |
| JSON-LD结构化数据 | 0.9% |
| 主体内容文字 | 1.3% |
| 标记、属性及其他一切 | 35.1% |
内联JavaScript是最大的单一板块:框架状态、配置和追踪代码直接嵌入页面中。现代网站通常会将整个页面数据作为JSON blob放在script标签中发送,再在客户端渲染,这就是为什么脚本的体积会超过它们最终展示的文字。
这也是一个机会。这些页面上的JSON-LD平均不到HTML的1%,而当页面以JSON形式嵌入数据时,该blob通常比渲染后的标记更容易解析。我们关于提取JSON-LD而非解析HTML和找到页面背后的API的指南涵盖了这两种方法。
在这一层,压缩比其他任何因素都更重要。 中位数页面在传输中缩小了5.4倍。278个内容页面中只有10个以未压缩形式提供,但如果抓取工具不请求压缩,每次都会获得完整的260 KiB。如果你的HTTP客户端发送Accept-Encoding: gzip, deflate, br,你实际支付的是46 KiB,而不是260 KiB。
浏览器层
在浏览器中渲染页面会下载页面所请求的一切,而不仅仅是文档本身。
| 衡量指标(每个内容页面) | 数值 |
|---|---|
| 浏览器中测量的页面数 | 274 |
| 完整加载时传输字节数的中位数 | 2.6 MB |
| 页面中间50%的范围 | 1.2 至 4.3 MB |
| 完整加载时请求数的中位数 | 81 |
| HTML文档占完整加载的比例(中位数) | 3% |
| 主体内容文字占完整加载的比例(中位数) | 0.13% |
按资源类型划分,在所有完整加载中:
| 资源类型 | 字节占比 |
|---|---|
| 图片 | 38.1% |
| 脚本 | 35.9% |
| 媒体(视频和音频) | 7.4% |
| 字体 | 6.6% |
| 样式表 | 2.8% |
| HTML文档 | 2.4% |
| API调用(fetch和XHR) | 4.9% |
| 其他 | 1.9% |
屏蔽图片、媒体和字体(抓取工具几乎从不需要这些)会将中位数页面降至1.5 MB和55个请求。在所有页面中,屏蔽后的加载传输了完整加载52%的字节。脚本更难安全地屏蔽,因为页面通常需要它们来渲染你想要的内容。
这对抓取预算意味着什么
假设一项任务每月收集一百万个内容页面,并只保留其主体文字。使用上述中位数:
| 页面获取方式 | 每百万页面的流量 |
|---|---|
| 仅主体文字,假设只能获取这些 | 约3.2 GB |
| 仅HTML,已压缩 | 约47 GB |
| 仅HTML,未压缩 | 约265 GB |
| 浏览器,屏蔽图片、媒体和字体 | 约1.5 TB |
| 浏览器,完整加载 | 约2.6 TB |
第一行与最后一行之间的差距大约是800倍。由此可得出切实可行的节省顺序:
- 只要数据在HTML中,就无需浏览器直接获取HTML。 仅此一项就是千兆字节与太字节之间的差别。
- 始终请求压缩。 这能以零成本将HTML流量减少约五倍。
- 当必须渲染时,屏蔽图片、媒体和字体。 这大约能将浏览器流量减半。
- 寻找同一数据的更小来源: JSON-LD、嵌入的JSON blob,或页面自身调用的API。
我们关于降低代理带宽成本的指南详细介绍了这些实践技巧,估算你的月度带宽则展示了如何将页面重量转化为计划。
测量你自己的页面
顶级站点的中位数是一个起点;你的目标站点才是真正重要的。下面的函数会获取一个页面,并报告其字节的去向,使用的方法与本研究相同:
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()),
}
它会在解压前读取响应正文,因此wire_bytes就是你实际传输的字节数,然后再手动解码。在每个目标站点的几个页面上运行它:
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是一个高效的页面:其主体文字超过HTML的11%,接近我们测得中位数的十倍。你的目标站点会落在这个区间的某处,而知道具体位置能告诉你,仅获取HTML、压缩,还是换一个数据源,哪种方式能节省最多。
测量的局限性
- 仅限顶级站点。 前1,000个域名都是大型、工程完善的站点。较小的站点可能更轻量,也可能重得多。
- 每个站点仅一个内容页面。 我们追踪了每个首页上第一个文章类型的链接。产品页面、搜索结果和列表页面可能有所不同。
- 主体内容是一个估计值。 提取库可能遗漏或过度包含内容,尤其是在主要由导航组成的页面上。应将主体文字数据视为近似值;字节测量则是精确的。
- 单次快照,单一网络。 页面于2026年10月8日仅获取了一次,来自欧洲的单一网络。网站会根据位置和时间提供不同的页面、广告和媒体。
- 浏览器加载有上限。 我们在load事件之后三秒停止测量。之后仍继续加载内容的页面传输的数据会比我们记录的更多。
常见问题
一个网页中实际内容占多大比例?
在前1,000个站点中的中位数内容页面上,主体文字约占HTML的1.2%,约占压缩传输字节的6%。在完整浏览器加载中,约占字节的0.13%。
屏蔽图片能减少代理带宽吗?
可以。在无头浏览器中屏蔽图片、媒体和字体,在我们的样本中减少了48%的字节。仅图片就占浏览器流量的38%。
不使用浏览器抓取更便宜吗?
通常会便宜很多。中位数内容页面作为压缩HTML只有46 KiB,而完整浏览器加载则有2.6 MB,差距超过50倍。
抓取时应该请求压缩响应吗?
是的。中位数页面压缩后缩小了5.4倍。大多数HTTP客户端默认会请求压缩,但请检查,因为不请求压缩的客户端会为每个页面的全部体积付费。
结论
抓取工具保留的数据只是它下载内容中的极小一部分:典型页面HTML的约1%,以及浏览器拉取内容的不到1%。这其中的大部分开销是可以避免的。能用HTML获取就不要渲染,保持压缩开启,必须渲染时屏蔽重型资源,并寻找能以少得多的字节承载同样信息的结构化数据或API。
来源与参考文献
- Tranco,列表Q2K34,用于本样本的面向研究的顶级站点排名。
- Barbaresi, A. (2021). Trafilatura: A Web Scraping Library and Command-Line Tool for Text Discovery and Extraction. ACL 2021 System Demonstrations.
- 由Shifter于2026年10月8日使用上述代码和无头Chromium测量的页面。