🎯 カスタマイズ可能で検出回避型のクラウドブラウザ。自社開発のChromiumを搭載し、ウェブクローラーAIエージェント向けに設計されています。👉今すぐ試す
ブログに戻ります

Playwrightにおけるリソースのブロック: 実際に何が節約されるのか

05-Aug-2026

TL;DR:

  • 「画像をブロックして帯域を節約する」はページに完全に依存します。 3つのページで測定した結果、画像、メディア、フォントをブロックすると、画像が多いカタログでは67%、記事ページでは約25%、画像のないテキストのみのリストではほとんど何も影響がありませんでした。
  • テキストのみのページではスタイルシートがペイロードでした。 ブロックリストにstylesheetを追加すると、それは2,121バイトに減少しました — これはHTMLドキュメント単独のサイズです — その理由は、4つのリクエストのうち3つがCSSだったからです。
  • バイトの節約は信頼できるが、時間の節約はそうではない。 ブロックされたプロファイルはすべて少なくなりましたが、実際の所要時間は実行ごとに両方向に変動しました。すべてのリクエストを監視するにはコストが伴います。
  • ブロックするとデータは消えません。 抽出された要素のカウントは9回の測定すべてで同じ — 20製品、10引用、36段落 — であり、有用なものは捨てられませんでした。
  • 相対的なパーセンテージではなく絶対的なバイトを測定し、複数回測定する。 ここにある1ページは、見かけ上の節約を1%から65%まで揺れ動かす二峰性のベースラインがありますが、コンテンツは変わりません。
  • 無料から始める。 Scraping Browserには無料プランがあり、この記事での測定は9ページのロードです。

PlaywrightはWebSocket CDPエンドポイントを介してScrapeless Scraping Browserに接続し、クラウドブラウザセッションはローカルのように振る舞います — そしてリモートのように請求されます。ページが引き出すすべての画像、フォント、スタイルシートは、あなたが移動するために支払ったトラフィックです。

標準的なアドバイスは画像をブロックすることです。そのアドバイスは数字なしで至る所で繰り返されるため、この投稿では数値を付けます:同じスクリプト、3ページ、3つのブロックプロファイル、プロトコルから数えたバイト数です。

ブラウザセッションの実際のコスト

ブラウザは人間が見るすべてを取得します。抽出のためには、その多くが無駄です — 誰もウェブフォントを解析しません。ウェブ全体のページ重量はHTTP Archiveページ重量レポートによって公表されており、そのような集計数字が画像ブロックのアドバイスの基盤となっています。

集計はそのアドバイスが間違うところです。中央値のページをスクレイピングするわけではなく、一つのターゲットをスクレイピングします。そのアセットミックスは中央値とはまったく異なる場合があります。

前提条件

  • Python 3.9以降およびpip install playwright
  • SCRAPELESS_API_KEY環境変数にScrapeless APIキーを持つこと

ローカルブラウザのダウンロードは必要ありません。connect_over_cdpはリモートセッションに接続するため、playwright installはこのワークフローの一部ではありません。

接続してバイトをカウントする

PlaywrightはCDPを通じて接続し、同じページで開かれた生CDPセッションはワイヤを横切るものをカウントします:

python Copy
browser = await playwright.chromium.connect_over_cdp(ENDPOINT)
page = await browser.new_page()
cdp = await page.context.new_cdp_session(page)
await cdp.send("Network.enable")

transferred = {"bytes": 0}
cdp.on(
    "Network.loadingFinished",
    lambda event: transferred.__setitem__(
        "bytes", transferred["bytes"] + event.get("encodedDataLength", 0)
    ),
)

encodedDataLengthは重要な測定値です。Chrome DevTools ProtocolのNetworkドメインは、それをリクエストに対して受信したバイトの合計数として定義しています。これにより、圧縮転送がカウントされ、非圧縮のドキュメントサイズはカウントされません。これがプロキシが測定するものであり、帯域幅の請求が反映されるものです。

Network.loadingFinishedで合計を出すと、ページのロードごとに1つの数値が得られ、パイプラインのどこにも推定はありません。

ルーティングレイヤーを追加

ブロックはリクエストごとのルーティングの決定です:

python Copy
BLOCKED = {"image", "media", "font"}

async def router(route):
    if route.request.resource_type in BLOCKED:
        await route.abort()
    else:
        await route.continue_()

await page.route("**/*", router)

すべてのリクエストは現在routerを通過し、そこで中止されるか、続行されます — PlaywrightページルーティングAPIのハンドラー契約です。何も行わないハンドラーはナビゲーションがタイムアウトするまでリクエストを待たせるため、各ブランチはabortまたはcontinue_で終わらなければなりません。

resource_typeはブラウザ自身がリクエストが行われた理由を分類するもので、Fetch Standardがリクエストの宛先と呼ぶ同じ概念です。それにマッチすることは、拡張子でURLをマッチさせるよりも耐久性があります。なぜなら、拡張子がなくても/assets/a8f3c2から配信されたフォントは依然としてfontとして分類されるからです。
これは、トラフィックを停止するのではなく、読むことの鏡像です — ページが行うリクエストからデータを抜き出す方法については、ページの隠れたJSON APIのインターセプトを参照してください。

三つのページを測定する

完全なスクリプトは、各ターゲットを三つのプロファイルで読み込み、バイト、節約、時間、まだ抽出可能な要素の数を印刷します。対照的な2つのフィクスチャだけでポイントを明確にできます;同様の方法で測定するために、自分のターゲットをTARGETSに追加してください:

python Copy
import asyncio
import os
import time

from playwright.async_api import async_playwright

ENDPOINT = (
    "wss://browser.scrapeless.com/api/v2/browser"
    f"?token={os.environ['SCRAPELESS_API_KEY']}&session_ttl=300&proxy_country=US"
)

TARGETS = [
    ("books.toscrape.com", "https://books.toscrape.com/", "article.product_pod"),
    ("quotes.toscrape.com", "https://quotes.toscrape.com/", "div.quote"),
]

PROFILES = {
    "baseline": set(),
    "media-only": {"image", "media", "font"},
    "media+css": {"image", "media", "font", "stylesheet"},
}

PAUSE_SECONDS = 5


async def measure(playwright, url, selector, blocked):
    """1ページを読み込み、(転送されたバイト数、秒、マッチした要素数)を返す。

    各測定は独自の接続を持つため、プロファイル間でHTTPキャッシュは共有されません;
    温かいキャッシュは、冷たいロードが実際にかかるバイト数を過小評価することになります。
    """
    browser = await playwright.chromium.connect_over_cdp(ENDPOINT)
    try:
        page = await browser.new_page()
        cdp = await page.context.new_cdp_session(page)
        await cdp.send("Network.enable")

        transferred = {"bytes": 0}
        cdp.on(
            "Network.loadingFinished",
            lambda event: transferred.__setitem__(
                "bytes", transferred["bytes"] + event.get("encodedDataLength", 0)
            ),
        )

        if blocked:
            async def router(route):
                if route.request.resource_type in blocked:
                    await route.abort()
                else:
                    await route.continue_()

            await page.route("**/*", router)

        started = time.monotonic()
        await page.goto(url, wait_until="load", timeout=120_000)
        elapsed = time.monotonic() - started
        matched = await page.locator(selector).count()
        return transferred["bytes"], elapsed, matched
    finally:
        await browser.close()


async def main():
    print(f"{'target':22}{'profile':12}{'bytes':>10}{'saved':>7}{'time':>8}{'matched':>9}")
    async with async_playwright() as playwright:
        for name, url, selector in TARGETS:
            baseline_bytes = None
            for profile, blocked in PROFILES.items():
                # 読み込みを間隔を置いて行います:これにより、各測定ごとに新しいセッションが開かれ、
                # 9回連続してページを読み込むのはターゲットに対して礼儀正しくありません。
                await asyncio.sleep(PAUSE_SECONDS)
                transferred, elapsed, matched = await measure(playwright, url, selector, blocked)
                if baseline_bytes is None:
                    baseline_bytes = transferred
                saved = round((1 - transferred / baseline_bytes) * 100)
                print(f"{name:22}{profile:12}{transferred:>10,}{saved:>6}%{elapsed:>7.2f}s{matched:>9}")


if __name__ == "__main__":
    asyncio.run(main())

実行例:

text Copy
target                profile          bytes  saved    time  matched
books.toscrape.com    baseline       337,691     0%   4.22s       20
books.toscrape.com    media-only     111,939    67%   5.62s       20
books.toscrape.com    media+css       76,329    77%   2.56s       20
quotes.toscrape.com   baseline        26,731     0%   5.86s       10
quotes.toscrape.com   media-only      26,739     0%   5.22s       10
quotes.toscrape.com   media+css        2,125    92%   3.12s       10

quotes.toscrape.comのブロックされた二行を念頭に置いてください:画像、メディア、フォントをブロックしても何も節約できず、その上スタイルシートをブロックすることでほぼ全てを節約できました。次のセクションではその理由を説明します — また、同じページが時々65%の節約を報告する理由も説明します。

数字が示すもの

割合はこの内容を読む自然な方法であり、それは最も不安定な要素です。ブロックされたプロファイルは非常に再現性があります;ベースラインはそうではなく、割合はその二つの比率です。
カタログページはほぼ完璧に再現可能で、その3つの基準測定値は互いに0.004%以内に収まりました。百科事典の記事は実行ごとに約7%の差がありました。そして、テキストリストは2つのモードがあることが分かりました。ほとんどの負荷は約26,700バイトを転送しますが、約3回に1回の負荷は約75,820バイトを転送します。これは、ブラウザがスタイルシートが参照するフォントバイナリを時々取得したり、時々しなかったりするためです。それに基づく測定された「節約」は、ブロッキングメディアによって1%から65%の間で変動し、ページの実際のコンテンツは変わりません。

ですので、絶対的な列を最初に読むことをお勧めします。3回の実行の中央値を、同じ方法で測定された実世界の百科事典の記事を第三のデータポイントとして追加しました:

ページ ベースライン ブロッキングメディア ブロッキングメディア + CSS 抽出された要素
books.toscrape.com — 画像カタログ 337,692 B 111,970 B 76,237 B 20 / 20 / 20
quotes.toscrape.com — テキストリスト 27,004 B 26,725 B 2,121 B 10 / 10 / 10
en.wikipedia.org — 記事 473,437 B 359,075 B 358,770 B 36 / 36 / 36

カタログページは3タイプのブロックリストによって重量の3分の2を失い、さらに10ポイントをスタイルシートに奪われます—これは基準に対して67%および77%のカットであり、その数値を信頼できるほど安定しています。

テキストリストはメディアがブロックされるとほとんど変化せず、スタイルシートもブロックされると2,121バイトに崩れます。その数はHTML文書自体であり、CSSをブロックしたすべての実行で同一でした。リクエストごとの内訳がその理由を説明しています:ページは4つのリクエストを行い、そのうち3つは約24,500バイトのスタイルシートで、2,121バイトのマークアップに対して合計します。これには全く画像がありません。

記事ページはメディアブロックに対して4分の1を失い、スタイルシートには測定可能なものがありません—中央値は305バイト動きましたが、media+css実行自体は29,000を超えました。このページでは、スタイルシートをブロックすることは節約にはなりません。

抽出はすべての場所で成功しました。20製品、10引用、および36段落はすべてのプロファイルで戻ってきたため、これらの節約はフィールドを犠牲にしませんでした。

タイミングには独自の警告が必要です。すべてのプロファイルは、上記の実行でのベースラインよりも速くなりましたが、これはまさにスピードアップを約束する誘惑になります。中央値セット全体ではそれは成り立ちません:カタログページはmedia+cssで4.66秒かかり、3.90秒のベースラインに対して、ウィキペディアはmedia-onlyで6.57秒にかかり、5.86秒と比較して—どちらも転送量が少ないのに遅くなっています。すべてのリクエストをPythonのコールバックを介してルーティングすると、リクエストごとにラウンドトリップが加わり、そのオーバーヘッドはブロックされた資産が運ぶはずだったバイト数とは無関係です。バイトの削減を信頼できる利益、レイテンシを目標ごとに測定する副作用と見なしてください。

始めるにはカードは不要です—無料プランは、このような9つの負荷の測定をカバーしています。

対象ごとのプロファイルを選択

測定は対象ごとに約1分かかり、推測作業を置き換えます:

  • ベースラインと1つのブロックプロファイルを代表的なページに対して実行 — カテゴリリストを使用し、ホ-ムページではありません。なぜなら、サイト全体で資産の構成が異なるためです。
  • デフォルトでimagemediafontをブロックします。 これらは決して解析されず、欠点は限られています。
  • stylesheetを別々にテストします。 ここでのあるページでは最大の利益を得ましたが、別のページでは無関係でした。レイアウト依存の抽出が確認すべきことです:クラスや構造に基づいているセレクタには影響がありませんが、計算されたジオメトリや可視性に依存するものは影響を受けません。
  • 必要なページでscriptdocumentをブロックしないでください。 クライアントがレンダリングしたページは、破棄されるはずのスクリプトを使ってコンテンツを構築します。
  • 対象が再設計されるときには再測定します。 プロファイルは資産の構成に調整されており、資産の構成は変わります。

ページがレンダリングを必要とするが、インタラクションが不要な場合は、HTTPレンダリング呼び出しを使用してブラウザセッションを完全に回避します—Scraping Browser製品ページと接続のドキュメントは、完全なセッションがそのコストを得るときについて説明します。

トラブルシューティング

ルーティングが有効になると、ナビゲーションがタイムアウトします。 ハンドラー内のコードパスは、abortcontinue_を呼び出さずに終了します。例外パスを含むすべてのブランチは、経路を解決する必要があります。

ブロックされたリクエストがバイトカウントにまだ表示されます。 Network.enableは、page.routeが登録された後、または測定されているページとは異なるCDPセッションで送信されました。ページの独自のコンテキストからセッションを作成し、ナビゲーションの前にドメインを有効にしてください。
ページが空白で表示され、セレクタが何も一致しません。 scriptまたはdocumentがブロックリストに入っています。両方ともクライアントレンダリングページには必要です。

バイト数は実行ごとに数パーセント以上のばらつきがあります。 ページは毎回同じアセットを取得していません。ここで測定されたテキストリストでは、スタイルシートによって参照されるフォントバイナリが一部の読み込みで取得され、他の読み込みでは取得されなかったため、ベースラインが約26,700バイトから約75,820バイトに移動しました。いくつかの実行の中央値を取り、割合ではなく絶対バイト数を比較してください。移動するベースラインは比率を歪めます。

節約額は大きく見えますが、記録が失われます。 スクリプトと同様に、抽出したカウントをベースラインと比較してからプロファイルを信頼してください。バイト数を削減し、記録を失うプロファイルは最適化ではありません。

結論

帯域幅は収集するページごとに増加し、ほとんどのコストとは異なり、約1分で測定できます。2行のCDPは、特定のターゲットに対してブロックリストを正当化するかどうかを示す数字を生成します。

ここでの数字が示すのは、デフォルトに対する反論です。画像をブロックすることは1ページで決定的であり、別のページでは中程度、3ページ目では画像がないため無関係でした。一方、このセットでの最大の勝利は、その3ページ目でスタイルシートをブロックすることで得られました。自分のターゲットに対してベースラインを実行する前に、いずれかのブロックリストを採用するのは避けてください — このリストを含めて、複数回実行してください。なぜなら、これら3ページのうち1ページでは同じ答えが2回得られないからです。

自分のターゲットを測定する準備はできましたか? 無料のScrapelessアカウントを作成し、キーをエクスポートし、すでに収集しているページに対してスクリプトを実行してください。プランの制限は料金ページにあり、プロキシ設定は同じ請求の出口側をカバーしています。

FAQ

Q: スクレイピング時にブロックしても安全なリソースタイプは何ですか?

imagemedia、およびfontはほとんどの抽出作業で安全です。なぜなら、どのパーサーもそれらを読み取らないからです。stylesheetは、セレクタが計算されたレイアウトではなくクラスと構造に依存する場合に安全で、これはほとんどのスクレイピングをカバーし、これらの測定での最大の節約となりました。クライアントサイドでコンテンツを構築するページでは、scriptdocumentxhr、およびfetchはそのままにしておいてください。

Q: 画像をブロックするとどれくらいの帯域幅を節約できますか?

完全にページに依存します。そのため、単一の数字は誤解を招きます。ここで測定された同じブロックリストは、画像カタログで67%、百科事典の記事で約4分の1、画像のないテキストのみのリストではほぼ何も節約できません。自分のターゲットに対してベースラインを実行し、絶対バイト数を比較してください — 1分の測定が公表された割合より優れています。

Q: リソースをブロックするとスクレイピングは速くなりますか?

バイト数が減少するのに比べて信頼性は低いです。ここでの各プロファイルは、ベースラインより少ない転送を行いましたが、実測時間は両方向に変動しました。なぜなら、各リクエストをハンドラーを通じてルーティングすることには往復がかかるからです。重いアセットが少ないページでは、そのオーバーヘッドが節約を上回ることがあるので、待機時間を仮定するのではなく検証すべきものと考えてください。

Q: page.routeまたはCDPのNetwork.setBlockedURLsを使用すべきですか?

page.routeはリソースタイプに基づいてマッチし、通常はそれが必要なものであり、ターゲットごとに簡単に変更できるPythonのロジックを保持します。CDPのブロックコマンドはURLパターンにマッチするため、特定の既知のホスト、例えばアナリティクスエンドポイントをブロックするのに適していますが、全体のアセットクラスをブロックするためには不向きです。両方が必要な場合には組み合わせて使用できます。

Q: リソースをブロックするとページの動作が実際のブラウザと異なるようになりますか?

はい、画像を要求しないセッションは通常のブラウザの動作ではないという意味で異なります。抽出に実際に必要なものにブロックリストを制限し、ブロックを有効にした後にターゲットの動作が変わった場合は、リストを狭めて再測定することをお勧めします。

Q: CDPセッションなしでバイトをカウントするにはどうすればよいですか?
それを近似するには、Playwrightのresponseハンドラーでレスポンスボディの長さを合計する方法がありますが、これは転送されたバイトではなく、解凍後のサイズであり、失敗したリクエストやキャッシュから提供されたリクエストを見逃してしまいます。ページ内の代替手段は、W3Cリソースタイミング仕様によって定義されたtransferSize属性で、performance.getEntriesByType("resource")を通じて読み取ることができ、正確な数量に近いですが、サードパーティのアセットに対してはゼロにされてしまうクロスオリジン制限の影響を受けます。Network.loadingFinishedencodedDataLengthは、オリジンに関係なくブラウザ自体が各リクエストのためにワイヤ上に記録したものであるため、追加の二行の価値があります。

Scrapelessでは、適用される法律、規制、およびWebサイトのプライバシーポリシーを厳密に遵守しながら、公開されているデータのみにアクセスします。 このブログのコンテンツは、デモンストレーションのみを目的としており、違法または侵害の活動は含まれません。 このブログまたはサードパーティのリンクからの情報の使用に対するすべての責任を保証せず、放棄します。 スクレイピング活動に従事する前に、法律顧問に相談し、ターゲットウェブサイトの利用規約を確認するか、必要な許可を取得してください。

最も人気のある記事

カタログ