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

プレイグラウンド + スクラップレス スクラッピング ブラウザ: 完全な HAR セッションをキャプチャして再生

James Thompson
James Thompson

Scraping and Proxy Management Expert

30-Jul-2026

TL;DR:

  • HARファイルは構造化されたHTTPアーカイブであり、スクリーンショットやビデオではありません。 そのlog.entries配列は、キャプチャされたHTTPリクエストごとに1つのオブジェクトを保存し、リクエストとレスポンスのメタデータ、利用可能な場合は記録されたコンテンツを含みます。
  • PlaywrightはブラウザコンテキストレベルでHARファイルを記録します。 コンテキストを作成する際にrecord_har_pathを設定し、context.close()を呼び出してアーカイブをディスクにフラッシュします。
  • Scrapeless Scraping Browserはリモートブラウザセッションを提供します。 PlaywrightはCDPを介して接続し、ページを読み込み、動的リクエストをトリガーし、結果として得られたトラフィックをローカルに保存します。
  • HAR分析にはブラウザは必要ありません。 ファイルが存在すれば、Pythonの標準jsonモジュールを使ってURL、メソッド、ステータス、MIMEタイプ、ヘッダー、埋め込まれたレスポンスボディを調べることができます。
  • キャプチャされたリクエストはPlaywrightなしで再発行できます。 この例では、HTTP/2の擬似ヘッダーを削除し、コンテンツエンコーディングを再交渉した後に、urllibを使用して1つの公開JSONリクエストを再構築します。
  • HARリプレイには限界があります。 失効したクッキー、CSRFトークン、ベアラー認証、WebSocketフレーム、変更されたサーバーデータは、後のリクエストが元のレスポンスを再現するのを妨げる可能性があります。
  • 開始は無料です。 新しいScrapelessアカウントには、無料のScraping Browserランタイムが含まれています — app.scrapeless.comでサインアップ

はじめに:まずキャプチャし、その後何が重要かを決定する

HARファイルは、ブラウザセッション中に観察されたHTTPトランザクションのJSONアーカイブです。これはレンダリングされたページのスクリーンショット、DOMスナップショット、またはセッションビデオではありません。

キャプチャされた各リクエストは次の中にオブジェクトとして表示されます:

text Copy
log.entries

エントリーにはリクエストメソッド、URL、ヘッダー、クエリパラメータ、送信データ、レスポンスステータス、レスポンスヘッダー、タイミング情報、記録されたレスポンスコンテンツが含まれる場合があります。バイナリコンテンツはBase64のようなエンコーディングで表現されることがあり、テキストボディはエントリーの中に直接表示されることがあります。

このガイドでは、PlaywrightをScrapeless Scraping BrowserにChrome DevTools Protocolを介して接続し、record_har_pathを使用して完全なページ読み込みとスクロールシーケンスを記録し、その後ブラウザを閉じます。

ワークフローの後半はブラウザなしで行います:

  1. jsonでHAR構造を調べます。
  2. キャプチャされたAPIリクエストを見つけます。
  3. 再利用可能なヘッダーを再構築します。
  4. urllibで再発行します。
  5. 新しいJSONレスポンスとアーカイブに保存されたボディを比較します。

表示されるすべての結果は、キャプチャされたターゲットセッションからのものです。

HARアーカイブでできること

HARキャプチャは、重要なエンドポイントがページの読み込み前にわからない場合に便利です。

  • 完全なページ読み込みを監査します。 1つのセッションからHTML、スタイルシート、スクリプト、フォント、画像、およびAPIリクエストをレビューします。
  • 内部JSONエンドポイントを発見します。 キャプチャ後にアーカイブを検索し、どのリクエストが重要になるかを予測するのではなく行います。
  • ページ動作の不具合をデバッグします。 ブラウザが閉じた後にリクエストのURL、レスポンスステータス、ヘッダー、MIMEタイプ、およびボディを調べます。
  • ネットワーク証拠を保全します。 後で分析したり、別のマシンに転送したりできるポータブルなJSONアーティファクトを1つ保存します。
  • ページ読み込みの動作を比較します。 別々のセッションをキャプチャし、それらのリクエストセット、ステータス、またはレスポンスコンテンツを比較します。
  • キャプチャされたレスポンスデータを抽出します。 ページを再度読み込むことなく、埋め込まれたJSONまたはテキストボディを読み取ります。
  • 再構築可能なリクエストを復元します。 標準HTTPクライアントを使用して、公開またはまだ認証されているHTTPリクエストを再発行します。
  • 決定論的ブラウザテストを構築します。 Playwrightの独立したroute_from_har()機能を使用して、記録されたレスポンスをライブブラウザコンテキストに返します。

HARファイルは、広範なキャプチャがすでに知られているエンドポイントにリスナーを接続するよりも便利な時に最も価値があります。

HARキャプチャ、ビデオ録画、HARルーティングは異なる

この分野の3つの機能は、似たような言語を使用していますが、異なる問題を解決します。

機能 記録または実行する内容 その後にブラウザは必要か?
Playwright record_har_path HTTPリクエストとレスポンスデータをアーカイブ 不要、オフラインでの検査のため
Scrapelessセッション録画 レンダリングされたセッションの視覚的リプレイを作成 不要、ダッシュボード再生のため
Playwright route_from_har() ブラウザコンテキストによって行われたリクエストに対して記録されたレスポンスを提供 必要
このガイドのurllibの例 キャプチャされたリクエストをライブサーバーに対して再発行 不要

ライブインターセプション

ライブインターセプションは、リクエストが発生する際にそれを観察します。ターゲットエンドポイントやレスポンスパターンがすでに知られているときに、うまく機能します。

セッションが終了すると、リスナーによってキャプチャされなかったものはすべて消えてしまいます。

HARキャプチャ

HARキャプチャは、コンテキストのHTTPトラフィックを広く記録します。重要なエンドポイントはブラウザが閉じた後に選択できます。

これにより、HARキャプチャは次のような場合に適した選択肢となります:

  • セッション後のデバッグ
  • ネットワークインベントリ
  • API発見
  • ページによって読み込まれるすべてのリソースの監査
  • オフライン検査のためのレスポンスボディの保存

スクラペレスセッション録画

スクラペレススクレイピングブラウザは、別のネイティブセッション録画機能をサポートしています。これにより、レンダリングされたブラウザセッションの再生可能なビジュアル記録が生成されます。

これはHARファイルの代わりにはなりません。ビデオは画面に表示された内容を示しますが、HARは構造化されたHTTPトランザクションを公開します。

Playwrightの route_from_har()

Playwrightの route_from_har() は、保存されたHARレスポンスをライブブラウザコンテキストで行われたリクエストに戻します。これは、ブラウザテストでバックエンドの動作をモックするために一般的に使用されます。

このガイドの再生例は別のことをします:アーカイブから1つのリクエストを読み取り、urllibを使って新しいライブHTTPリクエストを送信します。

なぜスクラペレススクレイピングブラウザを介してHARをキャプチャするのか?

スクラペレススクレイピングブラウザは、PlaywrightがCDP経由で操作するリモートブラウザ環境を提供します。

ブラウザセッションはクラウドインフラストラクチャ上で実行され、接続パラメーターを通じて地理的プロキシ選択をサポートしています。Playwrightは通常のブラウザ、コンテキスト、ページ、およびHAR APIを依然として使用します。

このワークフローの責任の分担は単純です:

コンポーネント 責任
スクラペレススクレイピングブラウザ リモートChromiumセッションを実行し、CDPエンドポイントを提供
Playwright コンテキストを作成し、ページを操作し、HARを記録
ローカルファイルシステム session.harを保存
Python標準ライブラリ アーカイブを検査し、選択されたリクエストを再送信

HAR記録そのものはPlaywrightの機能です。Playwrightをスクラペレスに接続することで、ライブレンダリングステージが管理されたリモートブラウザに移動し、結果のアーカイブをPythonスクリプトが実行されているマシンに残します。

接続は、ブラウザツールがネットワークアクティビティを観察するために使用する同じChrome DevTools Protocol Networkドメインを使用します。

スクレイピングブラウザのクイックスタートでは、より広範なPlaywrightとPuppeteerの接続モデルについて説明しています。

前提条件

必要なもの:

  • Python 3.9以降
  • 再現に使用するPlaywright 1.59.0
  • スクラペレスアカウントとAPIキー
  • session.harが作成されるディレクトリへの書き込みアクセス
  • 公共のターゲットページへのネットワークアクセス

Playwright 1.59.0はPython 3.9以降を宣言しています。このバージョンを固定することで、このガイドのキャプチャ結果とインストールが一致します。

このワークフローでは、ローカルのChromeインストールは必要ありません。connect_over_cdp()は、すでにスクラペレスインフラストラクチャで実行されているブラウザに接続します。

検査およびリクエスト再送信スクリプトは、Pythonの標準ライブラリのみを使用します。Playwrightをインポートしたり、ブラウザ接続を開いたりすることはありません。

ステップ1 — Playwrightのインストール

録画実行に使用されるバージョンをインストールします:

bash Copy
pip install "playwright==1.59.0"

スクリプトはCDP経由で既存のリモートブラウザに接続するため、ローカルにインストールされたブラウザ実行可能ファイルを起動しません。

シェルにスクラペレスAPIキーを設定します:

bash Copy
export SCRAPELESS_API_KEY="your_scrapeless_api_key"

コードはソース管理に保存するのではなく、環境からキーを読み取ります。

ステップ2 — スクラペレスCDP URLの構築

スクレイピングブラウザ接続URLは、APIキー、セッションの有効期限、プロキシ場所をクエリパラメーターとして持っています:

python Copy
import os
from urllib.parse import urlencode

API_KEY = os.environ["SCRAPELESS_API_KEY"]


def scraping_browser_url(proxy_country="US", session_ttl=180):
    params = urlencode({
        "token": API_KEY,
        "sessionTTL": session_ttl,
        "proxyCountry": proxy_country,
    })
    return f"wss://browser.scrapeless.com/api/v2/browser?{params}"

この関数は、次の形式のURLを生成します:

text Copy
wss://browser.scrapeless.com/api/v2/browser?token=...&sessionTTL=180&proxyCountry=US

構成された3つの値は次の通りです:

  • token: スクラペレスAPIキー
  • sessionTTL: 最大セッション寿命
  • proxyCountry: 要求されたプロキシ国

URL構築を1つの関数にまとめることで、複数のキャプチャスクリプトで同じ接続設定を適用しやすくなります。

ステップ3 — ブラウザセッションのキャプチャ

Playwrightの record_har_path 設定は browser.new_context() に属し、connect_over_cdp() には属しません。

ブラウザ接続はChromiumへのアクセスを提供します。コンテキストは何が記録されるかを定義します。

python Copy
import os
from urllib.parse import urlencode

from playwright.sync_api import sync_playwright


API_KEY = os.environ["SCRAPELESS_API_KEY"]
HAR_PATH = "session.har"


def scraping_browser_url(proxy_country="US", session_ttl=180):
    params = urlencode({
        "token": API_KEY,
        "sessionTTL": session_ttl,
        "proxyCountry": proxy_country,
    })
    return f"wss://browser.scrapeless.com/api/v2/browser?{params}"
python Copy
with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(scraping_browser_url())

    context = browser.new_context(
        record_har_path=HAR_PATH,
        record_har_content="embed",
    )

    page = context.new_page()
    page.goto(
        "https://quotes.toscrape.com/scroll",
        wait_until="networkidle",
    )

    for _ in range(3):
        page.evaluate(
            "window.scrollTo(0, document.body.scrollHeight)"
        )
        page.wait_for_timeout(800)

    print(
        "スクロール後に表示される引用要素:",
        page.locator(".quote").count(),
    )

    context.close()
    browser.close()

print(
    "HARが書き出されました:",
    HAR_PATH,
    "-",
    os.path.getsize(HAR_PATH),
    "バイト",
)

ライブ実行の結果:

text Copy
スクロール後に表示される引用要素: 40
HARが書き出されました: session.har - 333269 バイト

ターゲットは最初に10個の引用をレンダリングしました。下部に近づくスクロールは、別の /api/quotes?page=N リクエストをトリガーし、4つのAPIページにまたがって合計40個の引用を表示しました。

スクリプトは、どのリクエストが後で重要になるかを予測する必要はありませんでした。HARは文書、スタイルシート、JavaScript、フォント、およびJSON呼び出しもキャプチャしました。

無料プランのAPIキーを取得してください: app.scrapeless.com

なぜ context.close() が必要か

HARはブラウザコンテキストが閉じると確定されます。

Playwrightは record_har_path をブラウザコンテキスト設定として文書化し、HARが保存されるには browser_context.close() が必要です。ブラウザ接続のみを閉じると、文脈のアーティファクトが優雅にフラッシュされない可能性があります。

正しいシャットダウン順序は次のとおりです:

python Copy
context.close()
browser.close()

したがって、context.close() の呼び出しはキャプチャ手順の一部であり、オプションのクリーンアップではありません。

record_har_content="embed" は、記録されたレスポンスコンテンツをHAR内部に保存し、別の補助ファイルに保存するのではありません。これにより、session.har は後でのJSON検査やボディ比較のために自己完結型になります。

歴史的なHARフォーマット草案は、トップレベルのlogオブジェクトとその必須のentries配列を定義しています。各エントリは1つのエクスポートされたHTTPリクエストを表します。

ステップ4 — ブラウザなしでHARを検査する

コンテキストが閉じた後、session.har はローカルのJSONドキュメントです。

次のスクリプトは、jsoncollections、およびpathlib のみを使用します:

python Copy
import json
from collections import Counter
from pathlib import Path


har = json.loads(Path("session.har").read_text())
entries = har["log"]["entries"]

print("エントリの合計:", len(entries))

by_type = Counter(
    entry["response"]["content"]["mimeType"].split(";")[0]
    for entry in entries
)

for mime_type, count in by_type.most_common():
    print(f"  {mime_type}: {count}")

print()

for entry in entries:
    request = entry["request"]
    response = entry["response"]

    print(
        f"{request['method']:4s} "
        f"{response['status']:3d}  "
        f"{request['url']}"
    )

キャプチャされたファイルには以下が含まれていました:

text Copy
エントリの合計: 10
  application/json: 4
  text/css: 3
  text/html: 1
  application/javascript: 1
  font/woff2: 1

GET  200  https://quotes.toscrape.com/scroll
GET  200  https://quotes.toscrape.com/static/bootstrap.min.css
GET  200  https://quotes.toscrape.com/static/main.css
GET  200  https://quotes.toscrape.com/static/jquery.js
GET  200  https://fonts.googleapis.com/css?family=Raleway:400,700
GET  200  https://fonts.gstatic.com/s/raleway/v37/1Ptug8zYS_SKggPNyC0ITw.woff2
GET  200  https://quotes.toscrape.com/api/quotes?page=1
GET  200  https://quotes.toscrape.com/api/quotes?page=2
GET  200  https://quotes.toscrape.com/api/quotes?page=3
GET  200  https://quotes.toscrape.com/api/quotes?page=4

10のエントリは5つのMIMEタイプをカバーしています:

  • 1つのHTMLドキュメント
  • 3つのCSSレスポンス
  • 1つのJavaScriptレスポンス
  • 1つのWebフォント
  • 4つのJSONレスポンス

/api/quotes にフィルタリングされたライブリスナーは、4つのJSONリクエストを観察したでしょうが、他の6つのリソースは無視されたでしょう。HARは、後での検査のためにすべての10を保持しました。

HARエントリ構造の理解

har["log"]["entries"]の各アイテムは、ネストされたリクエストおよびレスポンスオブジェクトを含みます。

簡略化されたエントリは次の形状を持ちます:

json Copy
{
  "request": {
    "method": "GET",
    "url": "https://example.com/api/data",
    "headers": []
  },
  "response": {
    "status": 200,
    "headers": [],
    "content": {
      "mimeType": "application/json",
      "text": "{}"
    }
  }
}

便利なリクエストフィールドには以下が含まれます:

  • method
  • url
  • headers
  • queryString
  • postData

便利なレスポンスフィールドには以下が含まれます:

  • status
  • statusText
  • headers
  • content
  • redirectURL
Copy
HARコンテンツは、すべてのエントリに直接読み取れるテキストとして保存されることが保証されているわけではありません。リソースやレコーダーによっては、`content.text`が存在しない場合や、デコードされたテキスト、またはその形式を示す`encoding`フィールドを持つエンコードされた表現がある場合があります。

## ステップ5 — HTTP/2 擬似ヘッダーの処理

キャプチャされた`page=1` API呼び出しのリクエストヘッダーには、次のような名前が含まれていました。

```text
:authority
:method
:path
:scheme

これらはHTTP/2 擬似ヘッダーフィールドです。

これらは、HTTP/1.1リクエストラインまたはターゲットに表示される制御情報を運びます:

  • :methodはリクエストメソッドを特定します。
  • :schemeはURIスキームを特定します。
  • :authorityはターゲットの権限を特定します。
  • :pathはパスとクエリを特定します。

擬似ヘッダーは通常のHTTPヘッダーフィールドではありません。urllibのようなHTTP/1.1志向のクライアントは、コロンで始まるヘッダー名を受け入れることができません。

リプレイスクリプトは、これらの意味をURLおよびメソッドに変換し、その後それらを通常のヘッダーのマッピングから省略しなければなりません。

ステップ6 — urllibを使用してキャプチャされたリクエストを再発行

選択された/api/quotes?page=1リクエストは、現在ローカルのJSONファイル内の辞書です。

以下のスクリプトは:

  1. キャプチャされたエントリを見つけます。
  2. そのメソッド、URL、およびヘッダーを読み込みます。
  3. HTTP/2擬似ヘッダーを削除します。
  4. accept-encodingを削除します。
  5. 新しいurllib.request.Requestを作成します。
  6. ライブおよびキャプチャされたレスポンスボディを解析します。
  7. 2つのPythonオブジェクトを比較します。
python Copy
import json
import urllib.error
import urllib.request
from pathlib import Path


har = json.loads(Path("session.har").read_text())
entries = har["log"]["entries"]

target = next(
    entry
    for entry in entries
    if entry["request"]["url"].endswith("page=1")
)

captured_request = target["request"]

# HTTP/2擬似ヘッダーはプロトコルのフレーミングを説明し、
# 通常のHTTP/1.1スタイルのヘッダーとしては
# 渡すことができません。
#
# accept-encodingも省略されているので、urllibはスクリプトが
# 直接デコードできるエンコーディングを交渉できます。
skip_headers = {"accept-encoding"}

headers = {
    header["name"]: header["value"]
    for header in captured_request["headers"]
    if not header["name"].startswith(":")
    and header["name"].lower() not in skip_headers
}

print("再発行したヘッダー数:", len(headers))

request = urllib.request.Request(
    captured_request["url"],
    headers=headers,
    method=captured_request["method"],
)

with urllib.request.urlopen(request, timeout=10) as response:
    if response.status != 200:
        raise urllib.error.HTTPError(
            captured_request["url"],
            response.status,
            "予期しないステータス",
            response.headers,
            None,
        )

    live_data = json.loads(response.read())

captured_content = target["response"]["content"]
captured_data = json.loads(captured_content["text"])

print("ステータス:", response.status, "-- ブラウザプロセスは実行中ではありません")
print(
    "最初の引用の著者:",
    live_data["quotes"][0]["author"]["name"],
)
print(
    "HARがすでにキャプチャしたレスポンスボディと一致:",
    live_data == captured_data,
)

ブラウザなしのリプレイは次のように出力されました:

text Copy
再発行したヘッダー数: 12
ステータス: 200 -- ブラウザプロセスは実行中ではありません
最初の引用の著者: アルバート・アインシュタイン
HARがすでにキャプチャしたレスポンスボディと一致: True

フィルタリングされたマッピングには12の通常のヘッダーが含まれていました。HTTP/2擬似ヘッダーとaccept-encodingは除外されました。

accept-encodingHTTPコンテンツネゴシエーションフィールドです。リプレイクライアントは、ブラウザのBrotliネゴシエーションを盲目的にコピーするのではなく、サポートしているコンテンツコーディングを宣伝できます。

ターゲットレスポンスは、この実行のためにHARに保存されたJSONボディと一致しました。その比較は、直列化された空白やキーのフォーマットではなく、解析されたPythonオブジェクトで行われました。

これはセマンティックリクエスト再構築であり、元のネットワーク交換のバイトごとの再現ではありません。新しいリクエストは、異なるHTTPバージョン、ヘッダーの並び順、圧縮交渉、接続、およびTLSセッションを使用できます。

受け取るもの

ワークフローは、3つの再利用可能なアーティファクトまたは結果を生成します:

ステージ 出力 ブラウザは必要ですか?
キャプチャ session.har はい
検査 リクエストインベントリとMIMEタイプサマリー いいえ
再発行 解析されたライブレスポンスとキャプチャされたボディ比較 いいえ

キャプチャされたセッションは次のように生成されました:

text Copy
HARサイズ: 333269 バイト
HTTPエントリ: 10
JSONエントリ: 4
スクロール後に表示された引用: 40
再発行されたリクエストのステータス: 200
キャプチャされた/ライブのJSON一致: True

これらの数字は、この特定のターゲットセッションを説明しています。異なるページ、ブラウザのバージョン、スクロールタイミング、プロキシの場所、またはページレスポンスが異なるリクエストセットやファイルサイズを生成する可能性があります。

スクリプトでの全シーケンスを確認する

別々のキャプチャ、検査、リプレイプログラムは理解しやすいですが、同じステージを組み合わせることもできます。

python Copy
import json
import os
import urllib.error
import urllib.request
from collections import Counter
from pathlib import Path
from urllib.parse import urlencode

from playwright.sync_api import sync_playwright


API_KEY = os.environ["SCRAPELESS_API_KEY"]
HAR_PATH = "session.har"


def scraping_browser_url(proxy_country="US", session_ttl=180):
    params = urlencode({
        "token": API_KEY,
        "sessionTTL": session_ttl,
        "proxyCountry": proxy_country,
    })
    return f"wss://browser.scrapeless.com/api/v2/browser?{params}"


# ステージ 1: キャプチャ。ブラウザが必要。
with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(
        scraping_browser_url()
    )

    context = browser.new_context(
        record_har_path=HAR_PATH,
        record_har_content="embed",
    )

    page = context.new_page()
    page.goto(
        "https://quotes.toscrape.com/scroll",
        wait_until="networkidle",
    )

    for _ in range(3):
        page.evaluate(
            "window.scrollTo(0, document.body.scrollHeight)"
        )
        page.wait_for_timeout(800)

    context.close()
    browser.close()

print("=== キャプチャ ===")
print(
    "HARを書き込みました:",
    HAR_PATH,
    "-",
    os.path.getsize(HAR_PATH),
    "バイト",
)


# ステージ 2: 検査。ブラウザは閉じられています。
har = json.loads(Path(HAR_PATH).read_text())
entries = har["log"]["entries"]

print("\n=== 検査 (ブラウザなし) ===")
print("合計エントリ:", len(entries))

by_type = Counter(
    entry["response"]["content"]["mimeType"].split(";")[0]
    for entry in entries
)

for mime_type, count in by_type.most_common():
    print(f"  {mime_type}: {count}")


# ステージ 3: 一つのリクエストを再発行します。ブラウザは閉じたままです。
target = next(
    entry
    for entry in entries
    if entry["request"]["url"].endswith("page=1")
)

captured_request = target["request"]
skip_headers = {"accept-encoding"}

headers = {
    header["name"]: header["value"]
    for header in captured_request["headers"]
    if not header["name"].startswith(":")
    and header["name"].lower() not in skip_headers
}

request = urllib.request.Request(
    captured_request["url"],
    headers=headers,
    method=captured_request["method"],
)

with urllib.request.urlopen(request, timeout=10) as response:
    if response.status != 200:
        raise urllib.error.HTTPError(
            captured_request["url"],
            response.status,
            "予期しないステータス",
            response.headers,
            None,
        )

    live_data = json.loads(response.read())

captured_data = json.loads(
    target["response"]["content"]["text"]
)

print("\n=== 再発行 (ブラウザなし) ===")
print("ステータス:", response.status)
print(
    "最初の引用の著者:",
    live_data["quotes"][0]["author"]["name"],
)
print(
    "キャプチャされたコンテンツと一致:",
    live_data == captured_data,
)

まとめて実行された結果は次のとおりです:

text Copy
=== キャプチャ ===
HARを書き込みました: session.har - 333259 バイト

=== 検査 (ブラウザなし) ===
合計エントリ: 10
  application/json: 4
  text/css: 3
  text/html: 1
  application/javascript: 1
  font/woff2: 1

=== 再発行 (ブラウザなし) ===
ステータス: 200
最初の引用の著者: アルバート・アインシュタイン
キャプチャされたコンテンツと一致: True

最初のステージだけがPlaywrightをインポートして使用します。後のステージはファイルと選択したライブHTTPエンドポイントで操作します。

HARがキャプチャするもの

HARファイルはブラウザコンテキストによって記録されたHTTPトランザクションを表します。

レコーダーと設定によって、エントリには以下のものが含まれる場合があります:

  • リクエストメソッドとURL
  • クエリ文字列パラメーター
  • リクエストヘッダー
  • リクエストクッキー
  • 投稿されたデータ
  • レスポンスステータス
  • レスポンスヘッダー
  • レスポンスクッキー
  • MIMEタイプ
  • レスポンスコンテンツ
  • 転送サイズ
  • タイミング情報
  • リダイレクト詳細
  • キャッシュ情報

record_har_content="embed"を使用すると、Playwrightは利用可能なレスポンスコンテンツをHAR内に保存します。埋め込まれたコンテンツがない場合、リクエストインベントリは依然として役立ちますが、アーカイブはオフラインの解析や比較に必要なレスポンスボディを含まない場合があります。

HARが保証しないもの

HARファイルはキャプチャされたHTTPアクティビティの耐久性のある記録ですが、すべてのブラウザの動作を完全に記録するものではありません。

WebSocketフレームはアーカイブされません

HARはリクエストとレスポンスのトランザクションモデルです。確立されたWebSocket接続内で運ばれるメッセージのシーケンスを保持しません。

通常のHTTPエンドポイントとライブWebSocketフィードを組み合わせたページには、別々のキャプチャメカニズムが必要です:

  • HTTPリクエストとレスポンストラフィックのためのHAR
  • ソケットメッセージのためのWebSocketフレームリスナー

最初の接続にはHTTPアップグレードが含まれる場合がありますが、継続的なフレームストリームは通常のHARリクエスト-レスポンスモデルの外にあります。

HARはDOMスナップショットではありません

ファイルはDOMスナップショットのように最終的なドキュメントツリーを保存しません。
レスポンスボディには元のHTMLまたはJSONが含まれる場合がありますが、その後のJavaScriptの変化、要素の状態、レンダリングされたレイアウト、およびユーザーが目にする外観は別の問題です。

HARは動画ではない

アーカイブには、ページに表示されたものの視覚的なタイムラインは含まれていません。

可視ブラウザ動作のレビューが目的の場合はScrapelessセッション録画を使用してください。構造化されたHTTPトラフィックを検査することが目的の場合はHARキャプチャを使用してください。

一部のコンテンツが欠落しているかエンコードされている可能性があります

HARはオプションのコンテンツフィールドをサポートしています。レコーダーはボディを省略でき、バイナリリソースはエンコードされる場合があります。

response.content.textを解析する前に、以下を確認してください:

  • textフィールドが存在すること。
  • コンテンツタイプが期待通りであること。
  • encodingフィールドが存在する場合は処理されること。
  • ボディが録音設定によって省略されていないこと。

ブラウザフリー再発行が機能する場合

キャプチャされたリクエストは、ライブサーバーが再構成されたリクエストを受け入れる場合に正常に再発行できます。

公開されている/api/quotesエンドポイントは、期限切れの認証セッションに依存しないため機能します。

元のリクエストが以下を使用している場合、再発行はより複雑になります:

  • セッションクッキー
  • CSRFトークン
  • 短期間のベアラ資格情報
  • 署名付きURL
  • セッションごとのリクエスト署名
  • ブラウザ内部にのみ保存された状態
  • 前のナビゲーションステップによって生成されたデータ
  • セッションごとにコンテンツが変わるリクエストボディ

HARは元の資格情報の値を保持することができますが、その有効性を延長することはできません。それらの値が期限切れになると、新しいブラウザセッションが必要になる場合があります。

HARデータを安全にリプレイする

HARファイルには機密のセッションデータが含まれることがあります。

リクエストとレスポンスのヘッダーには以下が含まれる可能性があります:

  • クッキー
  • 認証ヘッダー
  • APIキー
  • セッション識別子
  • 内部URL
  • エンドポイントから返される個人データ

HARファイルを機密のアーティファクトとして扱ってください:

  • 公共のリポジトリにコミットしないでください。
  • 共有する前に資格情報を削除してください。
  • アーカイブされた本番セッションへのアクセスを制限してください。
  • 不必要に完全なヘッダーをログに記録しないでください。
  • デバッグや監査作業に必要なキャプチャのみを保存してください。
  • プロジェクトの保持要件に従ってアーカイブを削除してください。

このチュートリアルのアーカイブは、公開された認証されていないデモページから来ています。認証されたアプリケーションに自動的に同じ前提を適用しないでください。

一般的な問題

HARファイルが表示されない

最も一般的な原因は、コンテキストを明示的に閉じずにブラウザを閉じたことです。

次を使用してください:

python Copy
context.close()
browser.close()

また、プロセスがHAR_PATHを含むディレクトリに書き込むことができることを確認してください。

HARにレスポンスボディが含まれていない

コンテキストが次を使用していることを確認してください:

python Copy
record_har_content="embed"

次に、entry["response"]["content"]["text"]が存在するかどうかを調べてください。一部のリソースは省略されるか、エンコーディングで表される場合があります。

urllibがヘッダー名を拒否する

名前が:で始まるヘッダーは削除してください。これらはHTTP/2の擬似ヘッダーであり、普通のヘッダーフィールドではありません。

URLとリクエストメソッドは、すでにそれぞれの関連する意味を持っています。

再発行されたレスポンスが予期せず圧縮される

リプレイクライアントが宣伝されたすべてのコンテンツコーディングをサポートしていない限り、ブラウザのaccept-encoding値を盲目的にコピーしないでください。

HTTPクライアントにデコード可能なエンコーディングを交渉させてください。

新しいレスポンスがHARと一致しない

不一致は、再構築コードが間違っていることを必ずしも意味しません。

サーバーは、変化するコンテンツ、タイムスタンプ、ランダム化されたフィールド、地域特有の結果、またはもはや有効でない資格情報に紐づけられたデータを返すことがあります。

常に同一のバイトを返すと仮定するのではなく、安定していることが期待されるフィールドを比較してください。

結論

Playwrightのrecord_har_pathは、ブラウザコンテキストを耐久性のあるHTTPアーカイブに変えます。

ワークフローには3つの明確なフェーズがあります:

  1. PlaywrightをScrapelessスクレイピングブラウザに接続し、ページセッションをキャプチャします。
  2. ブラウザを閉じて、log.entriesを通常のJSONとして検査します。
  3. urllibを使用して適格なリクエストを再構築し、そのライブレスポンスをキャプチャされたボディと比較します。

アーカイブの生成にはブラウザが必要ですが、一度context.close()がHARをフラッシュすれば、ファイルはPlaywrightのインストール、ブラウザプロセス、またはCDP接続がなくても別のマシンで解析できます。

この分離により、HARキャプチャはセッション後のデバッグ、内部APIの発見、ネットワーク監査、リクエストの再構築に役立ちます。また、HARがWebSocketフレーム、レンダリングされた視覚状態、またはキャプチャされた資格情報の将来の有効性を保持しないという制限も明確になります。
Scraping Browserの製品ページをレビューして、キャプチャワークフローの背後にあるブラウザセッション機能を確認し、デモンストレーションセッションからより大規模なキャプチャワークロードに移行するときのためにScrapelessの料金プランもチェックしてください。

Chrome DevToolsプロトコルの説明では、ブラウザ接続の下にあるプロトコルの詳細が説明されています。

実際のブラウザセッションをアーカイブする準備はできましたか?

Scrapelessコミュニティに参加して、Playwrightのキャプチャパターンやブラウザデバッグワークフローを他の開発者と比較しましょう:Discord · Telegram

app.scrapeless.comで無料のScraping Browserランタイムにサインアップし、キャプチャ、検査、およびリクエスト再構築のステップをプロジェクトに関連する公開ページに適応させてください。


よくある質問

Q: HARファイルとは何ですか?

HARファイルは、ブラウザセッション中にキャプチャされたHTTPトランザクションのJSONアーカイブです。そのlog.entries配列には、記録された各リクエストごとに1つのオブジェクトが含まれ、ネストされたリクエスト、レスポンス、タイミング、およびコンテンツ情報が格納されています。

HARファイルはスクリーンショット、ページビデオ、または完全なDOMスナップショットではありません。

Q: HARキャプチャはライブリクエストの傍受とは異なりますか?

HARキャプチャは、ブラウザコンテキストのHTTPトラフィックを広範囲に記録しますが、ライブ傍受はマッチするリクエストが発生する際に観察します。

ライブ傍受はエンドポイントが既に知られている場合に便利です。HARキャプチャは、重要なリクエストがセッション終了後にのみ発見される場合に役立ちます。

Q: HARファイルを読むのにブラウザは必要ですか?

いいえ。完成したHARファイルは、Pythonのjsonモジュールで読み取ることができる通常のJSONドキュメントです。

キャプチャ中はブラウザが必要ですが、オフライン検査中は必要ありません。

Q: HARキャプチャにはWebSocketトラフィックが含まれますか?

HARキャプチャは、確立されたWebSocket接続内で交換されるメッセージをアーカイブしません。

ページがライブソケットフィードに依存している場合は、WebSocketフレームリスナーを使用してください。HARは同じページで使用される通常のHTTPトラフィックをカバーすることができます。

Q: Playwright HAR録画はScrapelessセッション録画と同じですか?

いいえ。Playwright HAR録画は構造化されたネットワークアーカイブを作成しますが、Scrapelessセッション録画はレンダリングされたブラウザセッションの視覚的リプレイを作成します。

リクエストとレスポンスの分析にはHARを使用し、可視ページの動作が調査の対象である場合にはセッション録画を使用してください。

Q: urllibの例はPlaywrightのroute_from_har()と同じですか?

いいえ。route_from_har()は、ライブPlaywrightブラウザコンテキスト内で行われたリクエストに対して記録されたレスポンスを提供します。

urllibの例はHARからリクエストを読み取り、新しいリクエストをライブサーバーに送信しますが、ブラウザは起動しません。

Q: なぜHARには:authority:methodのようなヘッダーが含まれていますか?

これらの名前は、HTTP/2プロトコル内でリクエスト制御データを運ぶために使用されるHTTP/2の擬似ヘッダーフィールドです。

これらは通常のヘッダーフィールドではないため、HTTP/1.1スタイルのクライアントは、それらの意味をリクエストメソッドとURLを通じて表現する必要があります。

Q: キャプチャされたすべてのリクエストは正常に再発行できますか?

いいえ。リクエストは、ライブサーバーが再構築されたメソッド、URL、ボディ、ヘッダー、および資格情報を受け入れている間のみ再発行できます。

期限切れのクッキー、CSRFトークン、署名されたURL、または短命のベアラ資格情報に依存するリクエストは、新しいブラウザセッションを必要とする場合があります。

Q: このワークフローに別のプロキシが必要ですか?

Scrapeless Scraping Browserの接続がすでに希望するプロキシ国を指定している場合、別のプロキシ設定は必要ありません。

この例では、CDP接続URLにproxyCountry=USが設定されているため、ブラウザセッションはその構成されたエグレスを使用します。

Q: HARファイルを共有することは安全ですか?

HARファイルは、その内容がレビューされ消毒されるまで機密性の高いものとして扱われるべきです。

それには、クッキー、認証ヘッダー、APIキー、内部エンドポイント、送信されたフォームデータ、またはプライベートなレスポンス内容が含まれている可能性があります。共有する前に機密性の高い値を削除してください。

Q: HAR録画はScrapeless Scraping Browserでのみ機能しますか?

いいえ。record_har_pathはPlaywrightのブラウザコンテキストオプションであり、互換性のあるローカルまたはリモートのブラウザで使用できます。

Scrapeless Scraping Browserは、キャプチャ段階中に使用される管理されたリモートChromium環境とプロキシ設定を提供します。

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

最も人気のある記事

カタログ