プロキシのトラフィックをギガバイト単位で支払う場合、ページを構成するすべてのバイトは同じコストがかかる。目当ての段落も、その周りのメニューも、アナリティクススクリプトも、ヒーロー画像も、フォントファイルも。ほとんどのチームはページが重いことを知っている。しかし、ダウンロードしたもののうちどれだけが実際に保持するデータなのかを知っている人は少ない。
私たちはそれを測定した。2026年10月8日、Tranco ランキングの上位1,000ドメインのホームページを取得し、それぞれから記事スタイルのリンクを1つたどり、バイトがどこへ行くのかを測定した。通信経路上、HTML内、そしてブラウザがダウンロードするすべての中で。本稿はその結果、使用したコード、そしてそれがスクレイピング予算にとって何を意味するのかを共有する。
Key takeaways
- 典型的なページのメインコンテンツは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%であった。
How we measured
- サンプル。 Tranco リストの上位1,000ドメイン(リストQ2K34)。多くはAPI、CDN、トラッキング用ホストでウェブサイトを持たないため、431の異なるサイトが利用可能なホームページを返した。各ホームページから、コンテンツページらしき最初の同一サイト内リンク(4語以上のスラグで終わるパスで、ログイン、法務、ヘルプのページを除く)をたどり、278の利用可能なコンテンツページを得た。
- HTML層。 通常のデスクトップブラウザのUser-Agentと圧縮を有効にした状態で、ページごとに1回のリクエストを行った。受信したバイトを記録し、解凍した上で、HTMLをインラインスクリプト、インラインスタイル(
style属性を含む)、インラインSVG、JSON-LDに分割した。メインコンテンツはオープンソースの抽出ライブラリであるtrafilaturaを用いて抽出し、そのバイト数を正規化テキストとして数えた。 - ブラウザ層。 各コンテンツページをヘッドレスChromiumで読み込み、loadイベントに加えて3秒待機し、リソースタイプごとにすべてのレスポンスで転送されたバイト数を合計した。その後、画像、メディア、フォントをブロックした状態で再度読み込んだ。
- 除外対象。 エラーレスポンス、HTML以外のレスポンス、ブロックページ(チャレンジページ、「アクセス拒否」ページ、ほぼ空のレスポンス)は分析前に除外したため、結果は実際にコンテンツを提供したページを記述している。
The HTML layer
| Measure (median per page) | Content pages | Homepages |
|---|---|---|
| Pages measured | 278 | 431 |
| Bytes on the wire | 46 KiB | 51 KiB |
| HTML after decompression | 260 KiB | 270 KiB |
| Main content text | 3.2 KB | 1.4 KB |
| Main content as a share of the HTML | 1.2% | 0.6% |
| Main content as a share of bytes transferred | 6.2% | 3.1% |
| All visible text as a share of the HTML | 3.6% | 2.4% |
コンテンツページはホームページよりも多くのテキストを含んでいる、これは予想通りだが、全体の形は同じである。スクレイパーが保持するテキストは、ダウンロードするドキュメントのほんの一部にすぎない。メニュー、フッター、クッキー通知を含むページ上のすべての可視テキストを数えても、テキストはHTMLの4%未満であった。
残りはどこへ行くのか。278のコンテンツページ全体で見ると:
| Part of the HTML | Share of HTML bytes |
|---|---|
| Inline JavaScript | 37.5% |
| Inline CSS and style attributes | 15.4% |
| Inline SVG | 9.8% |
| JSON-LD structured data | 0.9% |
| Main content text | 1.3% |
| Markup, attributes and everything else | 35.1% |
インラインJavaScriptは単独では最大のブロックであり、フレームワークの状態、設定、トラッキングコードがページに直接埋め込まれている。最近のサイトはページデータ全体をscriptタグ内のJSONブロブとして出荷し、クライアント側でレンダリングすることが多く、これがスクリプトが最終的に表示されるテキストを上回る理由である。
これは機会でもある。これらのページにおけるJSON-LDの平均はHTMLの1%未満であり、ページがデータをJSONとして埋め込んでいる場合、そのブロブは通常、レンダリングされたマークアップよりもはるかにきれいにパースできる。HTMLをパースする代わりにJSON-LDを抽出する方法とページの背後にあるAPIを見つける方法についてのガイドで、両方を解説している。
この層では圧縮が他の何よりも重要である。 中央値のページは転送時に5.4倍縮小した。278のコンテンツページのうち圧縮されずに提供されたのはわずか10件であったが、圧縮を要求しないスクレイパーは毎回フルサイズの260 KiBを受け取ることになる。HTTPクライアントがAccept-Encoding: gzip, deflate, brを送信していれば、すでに260 KiBではなく46 KiBの分だけ支払っていることになる。
The browser layer
ブラウザでページをレンダリングすると、ドキュメントだけでなく、ページが要求するすべてがダウンロードされる。
| Measure (per content page) | Value |
|---|---|
| Pages measured in the browser | 274 |
| Median bytes transferred, full load | 2.6 MB |
| Middle half of pages | 1.2 to 4.3 MB |
| Median requests, full load | 81 |
| HTML document as a share of the full load (median) | 3% |
| Main content text as a share of the full load (median) | 0.13% |
リソースタイプ別に見ると、すべてのフルロードにおいて:
| Resource type | Share of bytes |
|---|---|
| Images | 38.1% |
| Scripts | 35.9% |
| Media (video and audio) | 7.4% |
| Fonts | 6.6% |
| Stylesheets | 2.8% |
| HTML documents | 2.4% |
| API calls (fetch and XHR) | 4.9% |
| Other | 1.9% |
スクレイパーがほとんど必要としない画像、メディア、フォントをブロックすると、中央値のページは1.5 MB、55リクエストにまで減少した。全ページにおいて、ブロックした読み込みはフルロードのバイト数の52%を転送した。スクリプトは安全にブロックすることがより難しい。ページが目当てのコンテンツをレンダリングするためにスクリプトを必要とすることが多いためである。
What it means for a scraping budget
月間100万件のコンテンツページを収集し、メインテキストのみを保持するジョブを考える。上記の中央値を用いると:
| How the pages are fetched | Traffic per million pages |
|---|---|
| Main text alone, if you could fetch only that | about 3.2 GB |
| HTML only, compressed | about 47 GB |
| HTML only, uncompressed | about 265 GB |
| Browser, images, media and fonts blocked | about 1.5 TB |
| Browser, full load | about 2.6 TB |
最初の行と最後の行の差はおよそ800倍である。節約の実用的な順序はそこから導かれる:
- データがHTMLの中にある場合は、常にブラウザなしでHTMLを取得する。 それだけでギガバイトとテラバイトの違いが生まれる。
- 常に圧縮を要求する。 コストをかけずにHTMLトラフィックを約5倍削減できる。
- レンダリングが必要な場合は、画像、メディア、フォントをブロックする。 ブラウザのトラフィックをほぼ半分に削減できる。
- 同じデータのより小さい出所を探す: JSON-LD、埋め込まれたJSONブロブ、またはページ自体が呼び出すAPIである。
プロキシの帯域幅コストを削減するガイドでは、これらの手法それぞれを実践的に解説しており、月間帯域幅を見積もるガイドでは、ページの重さを計画に落とし込む方法を示している。
Measure your own pages
上位サイト全体の中央値は出発点にすぎず、重要なのはあなたのターゲットである。以下の関数は、本研究と同じ方法を用いて、1ページを取得しそのバイトがどこへ行くかを報告する:
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%を超え、私たちが測定した中央値のほぼ10倍である。あなたのターゲットはその範囲のどこかに収まるはずであり、どこに位置するかを知ることで、HTMLのみの収集、圧縮、あるいは別のデータソースのどれが最も節約になるかがわかる。
Limits of the measurement
- 上位サイトのみ。 上位1,000ドメインは大規模で、よく作り込まれたサイトである。小規模サイトはより軽い場合も、はるかに重い場合もある。
- サイトごとに1つのコンテンツページ。 各ホームページの最初の記事スタイルのリンクをたどった。商品ページ、検索結果、一覧ページは異なる可能性がある。
- メインコンテンツは推定値である。 抽出ライブラリはコンテンツを見落としたり、過剰に含めたりすることがあり、特にナビゲーションが大半を占めるページでその傾向がある。メインテキストの数値は概算として扱うべきであり、バイトの測定値は正確である。
- 1回のスナップショット、1つのネットワーク。 ページは2026年10月8日に、ヨーロッパの単一ネットワークから一度だけ取得された。サイトは場所や時間によって異なるページ、広告、メディアを提供する。
- ブラウザの読み込みには上限を設けた。 loadイベントの3秒後に測定を停止した。その後もコンテンツを読み込み続けるページは、私たちが記録したよりも多くを転送していた可能性がある。
FAQ
ウェブページのうち実際のコンテンツはどれくらいの割合か?
上位1,000サイトの中央値のコンテンツページでは、メインテキストはHTMLの約1.2%、転送された圧縮バイトの約6%であった。フルブラウザ読み込みでは、バイト数の約0.13%であった。
画像をブロックするとプロキシの帯域幅は削減されるか?
はい。ヘッドレスブラウザで画像、メディア、フォントをブロックすると、サンプル全体でバイト数の48%が削減された。画像だけでブラウザトラフィックの38%を占めていた。
ブラウザなしでスクレイピングする方が安いか?
通常は大差で安い。中央値のコンテンツページは、圧縮HTMLとして46 KiB、フルブラウザ読み込みでは2.6 MBであり、50倍以上の差がある。
スクレイピング時に圧縮レスポンスを要求すべきか?
はい。中央値のページは圧縮により5.4倍小さくなった。ほとんどのHTTPクライアントはデフォルトで圧縮を要求するが、確認すべきである。要求しないクライアントは、すべてのページのフルサイズ分の代金を支払うことになるからである。
The bottom line
スクレイパーが保持するデータは、ダウンロードするもののごく一部にすぎない。典型的なページのHTMLの約1%であり、ブラウザが引き込むものの1%にも満たない割合である。その大部分のオーバーヘッドは回避可能である。可能な場合はレンダリングの代わりにHTMLを取得し、圧縮を常に有効にし、レンダリングが必要な場合は重いリソースをブロックし、同じ情報をはるかに少ないバイト数で運ぶ構造化データやAPIを探すことである。
Sources and references
- Tranco, list Q2K34、サンプルに使用された、研究向けの上位サイトランキング。
- Barbaresi, A. (2021). Trafilatura: A Web Scraping Library and Command-Line Tool for Text Discovery and Extraction. ACL 2021 System Demonstrations.
- 2026年10月8日に、上記のコードとヘッドレスChromiumを用いてShifterが測定したページ。