🎯 A customizable, anti-detection cloud browser powered by self-developed Chromium designed for web crawlers and AI Agents.👉Try Now
Back to Blog

Playwright + Scrapeless Scraping Browser: Capture and Replay a Full HAR Session

James Thompson
James Thompson

Scraping and Proxy Management Expert

30-Jul-2026

TL;DR:

  • A HAR file is a structured HTTP archive, not a screenshot or video. Its log.entries array stores one object per captured HTTP request, including request and response metadata and, when available, recorded content.
  • Playwright records HAR files at the browser-context level. Set record_har_path when creating the context and call context.close() to flush the archive to disk.
  • Scrapeless Scraping Browser supplies the remote browser session. Playwright connects to it over CDP, loads the page, triggers dynamic requests, and saves the resulting traffic locally.
  • HAR analysis does not require a browser. Once the file exists, Python’s standard json module can inspect its URLs, methods, statuses, MIME types, headers, and embedded response bodies.
  • A captured request can be reissued without Playwright. The example rebuilds one public JSON request with urllib after removing HTTP/2 pseudo-headers and renegotiating content encoding.
  • HAR replay has boundaries. Expired cookies, CSRF tokens, bearer credentials, WebSocket frames, and changing server data can prevent a later request from reproducing the original response.
  • Free to start. New Scrapeless accounts include free Scraping Browser runtime — sign up at app.scrapeless.com.

Introduction: Capture First, Decide What Matters Later

A HAR file is a JSON archive of HTTP transactions observed during a browser session. It is not a rendered-page screenshot, DOM snapshot, or session video.

Each captured request appears as an object inside:

text Copy
log.entries

An entry can contain the request method, URL, headers, query parameters, posted data, response status, response headers, timing information, and recorded response content. Binary content may be represented with an encoding such as Base64, while textual bodies can appear directly in the entry.

This guide connects Playwright to the Scrapeless Scraping Browser over the Chrome DevTools Protocol, records a complete page load and scroll sequence with record_har_path, and closes the browser.

The second half of the workflow is browser-free:

  1. Inspect the HAR structure with json.
  2. Find a captured API request.
  3. Rebuild its reusable headers.
  4. Reissue it with urllib.
  5. Compare the new JSON response with the body stored in the archive.

Every displayed result comes from the captured target session.

What You Can Do With a HAR Archive

HAR capture is useful when the important endpoint is not known before a page loads.

  • Audit a complete page load. Review HTML, stylesheets, scripts, fonts, images, and API requests from one session.
  • Discover internal JSON endpoints. Search the archive after capture instead of predicting which request will matter.
  • Debug failed page behavior. Inspect request URLs, response statuses, headers, MIME types, and bodies after the browser has closed.
  • Preserve network evidence. Store one portable JSON artifact that can be analyzed later or transferred to another machine.
  • Compare page-load behavior. Capture separate sessions and compare their request sets, statuses, or response content.
  • Extract captured response data. Read embedded JSON or text bodies without loading the page again.
  • Reconstruct eligible requests. Reissue public or still-authenticated HTTP requests with a standard HTTP client.
  • Build deterministic browser tests. Use Playwright’s separate route_from_har() feature to serve recorded responses back to a live browser context.

A HAR file is most valuable when broad capture is more useful than attaching a listener to one already-known endpoint.

HAR Capture, Video Recording, and HAR Routing Are Different

Three features in this area use similar language but solve different problems.

Feature What it records or does Browser required afterward?
Playwright record_har_path Archives HTTP request and response data No, for offline inspection
Scrapeless session recording Creates a visual replay of the rendered session No, for dashboard playback
Playwright route_from_har() Serves recorded responses to requests made by a browser context Yes
The urllib example in this guide Reissues one captured request against the live server No

Live Interception

Live interception observes requests as they occur. It works well when the target endpoint or response pattern is already known.

Once the session ends, anything not captured by the listener is gone.

HAR Capture

HAR capture records the context’s HTTP traffic broadly. The endpoint that matters can be selected after the browser closes.

This makes HAR capture a better fit for:

  • Post-session debugging
  • Network inventories
  • API discovery
  • Auditing all resources loaded by a page
  • Preserving response bodies for offline inspection

Scrapeless Session Recording

Scrapeless Scraping Browser supports a separate native session-recording capability. That produces a replayable visual record of the rendered browser session.

It does not replace a HAR file. A video shows what appeared on screen; a HAR exposes structured HTTP transactions.

Playwright route_from_har()

Playwright’s route_from_har() sends saved HAR responses back into requests made by a live browser context. It is commonly used to mock backend behavior in browser tests.

The replay example in this guide does something else: it reads one request from the archive and submits a new live HTTP request with urllib.

Why Capture HAR Through Scrapeless Scraping Browser?

Scrapeless Scraping Browser provides the remote browser environment that Playwright drives over CDP.

The browser session runs on cloud infrastructure and supports geographic proxy selection through the connection parameters. Playwright still uses its normal browser, context, page, and HAR APIs.

For this workflow, the division of responsibility is simple:

Component Responsibility
Scrapeless Scraping Browser Runs the remote Chromium session and provides the CDP endpoint
Playwright Creates the context, drives the page, and records the HAR
Local filesystem Stores session.har
Python standard library Inspects the archive and reissues the selected request

HAR recording itself is a Playwright feature. Connecting Playwright to Scrapeless moves the live rendering stage into a managed remote browser while leaving the resulting archive on the machine running the Python script.

The connection uses the same Chrome DevTools Protocol Network domain that browser tooling uses to observe network activity.

The Scraping Browser quickstart covers the broader Playwright and Puppeteer connection model.

Prerequisites

You need:

  • Python 3.9 or newer
  • Playwright 1.59.0 for the reproduced run
  • A Scrapeless account and API key
  • Write access to the directory where session.har will be created
  • Network access to the public target page

Playwright 1.59.0 declares Python 3.9 or newer. Pinning that version makes the installation match the captured results in this guide.

No local Chrome installation is required for this workflow. connect_over_cdp() attaches to a browser already running on the Scrapeless infrastructure.

The inspection and request-reissue scripts use only Python’s standard library. They do not import Playwright or open a browser connection.

Step 1 — Install Playwright

Install the version used for the recorded run:

bash Copy
pip install "playwright==1.59.0"

Because the script connects to an existing remote browser over CDP, it does not launch a locally installed browser executable.

Set the Scrapeless API key in the shell:

bash Copy
export SCRAPELESS_API_KEY="your_scrapeless_api_key"

The code reads the key from the environment rather than storing it in source control.

Step 2 — Build the Scrapeless CDP URL

The Scraping Browser connection URL carries the API key, session lifetime, and proxy location as query parameters:

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}"

The function generates a URL in this shape:

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

The three configured values are:

  • token: the Scrapeless API key
  • sessionTTL: the maximum session lifetime
  • proxyCountry: the requested proxy country

Keeping the URL construction in one function also makes it easier to apply the same connection settings across multiple capture scripts.

Step 3 — Capture the Browser Session

Playwright’s record_har_path setting belongs to browser.new_context(), not connect_over_cdp().

The browser connection provides access to Chromium. The context defines what will be recorded.

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}"


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(
        "quote elements visible after scrolling:",
        page.locator(".quote").count(),
    )

    context.close()
    browser.close()

print(
    "HAR written:",
    HAR_PATH,
    "-",
    os.path.getsize(HAR_PATH),
    "bytes",
)

The live run printed:

text Copy
quote elements visible after scrolling: 40
HAR written: session.har - 333269 bytes

The target initially rendered ten quotes. Each scroll near the bottom triggered another /api/quotes?page=N request, bringing the visible total to 40 quotes across four API pages.

The script did not need to predict which request would later matter. The HAR also captured the document, stylesheets, JavaScript, font, and JSON calls.

Get your API key on the free plan: app.scrapeless.com

Why context.close() Is Required

The HAR is finalized when the browser context closes.

Playwright documents record_har_path as a browser-context setting and requires browser_context.close() for the HAR to be saved. Closing only the browser connection can leave context artifacts without a graceful flush.

The correct shutdown order is:

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

The context.close() call is therefore part of the capture procedure, not optional cleanup.

record_har_content="embed" stores recorded response content inside the HAR rather than in separate companion files. This makes session.har self-contained for the later JSON inspection and body comparison.

The historical HAR format draft defines the top-level log object and its required entries array. Each entry represents one exported HTTP request.

Step 4 — Inspect the HAR Without a Browser

After the context closes, session.har is a local JSON document.

The following script uses only json, collections, and 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("total entries:", 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']}"
    )

The captured file contained:

text Copy
total entries: 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

The ten entries cover five MIME types:

  • One HTML document
  • Three CSS responses
  • One JavaScript response
  • One web font
  • Four JSON responses

A live listener filtered to /api/quotes would have observed the four JSON requests but ignored the other six resources. The HAR preserved all ten for later inspection.

Understanding the HAR Entry Structure

Every item in har["log"]["entries"] contains nested request and response objects.

A simplified entry has this shape:

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

Useful request fields include:

  • method
  • url
  • headers
  • queryString
  • postData

Useful response fields include:

  • status
  • statusText
  • headers
  • content
  • redirectURL

HAR content is not guaranteed to be stored as directly readable text in every entry. Depending on the resource and recorder, content.text may be absent, decoded text, or an encoded representation whose encoding field identifies the format.

Step 5 — Handle HTTP/2 Pseudo-Headers

The request headers for the captured page=1 API call included names such as:

text Copy
:authority
:method
:path
:scheme

These are HTTP/2 pseudo-header fields.

They carry control information that would appear in an HTTP/1.1 request line or target:

  • :method identifies the request method.
  • :scheme identifies the URI scheme.
  • :authority identifies the target authority.
  • :path identifies the path and query.

Pseudo-headers are not ordinary HTTP header fields. An HTTP/1.1-oriented client such as urllib cannot accept a colon-prefixed header name.

A replay script must translate their meaning into the URL and method, then omit them from the ordinary header mapping.

Step 6 — Reissue One Captured Request With urllib

The selected /api/quotes?page=1 request is now a dictionary inside a local JSON file.

The script below:

  1. Finds the captured entry.
  2. Reads its method, URL, and headers.
  3. Removes HTTP/2 pseudo-headers.
  4. Removes accept-encoding.
  5. Creates a new urllib.request.Request.
  6. Parses the live and captured response bodies.
  7. Compares the two Python objects.
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 pseudo-headers describe protocol framing and cannot be
# passed as ordinary HTTP/1.1-style headers.
#
# accept-encoding is also omitted so urllib can negotiate an
# encoding the script can decode directly.
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("replayed header count:", 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,
            "unexpected status",
            response.headers,
            None,
        )

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

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

print("status:", response.status, "-- no browser process running")
print(
    "first quote author:",
    live_data["quotes"][0]["author"]["name"],
)
print(
    "matches the response body the HAR already captured:",
    live_data == captured_data,
)

The browser-free replay printed:

text Copy
replayed header count: 12
status: 200 -- no browser process running
first quote author: Albert Einstein
matches the response body the HAR already captured: True

The filtered mapping contained 12 ordinary headers. The HTTP/2 pseudo-headers and accept-encoding were excluded.

accept-encoding is an HTTP content-negotiation field. The replaying client can advertise the content codings it supports instead of copying the browser’s Brotli negotiation blindly.

The target response matched the JSON body stored in the HAR for this run. That comparison was performed on the parsed Python objects rather than on their serialized whitespace or key formatting.

This is a semantic request reconstruction, not a byte-for-byte reproduction of the original network exchange. The new request can use a different HTTP version, header ordering, compression negotiation, connection, and TLS session.

What You Get Back

The workflow produces three reusable artifacts or results:

Stage Output Browser required?
Capture session.har Yes
Inspection Request inventory and MIME-type summary No
Reissue Parsed live response and captured-body comparison No

The captured session produced:

text Copy
HAR size: 333269 bytes
HTTP entries: 10
JSON entries: 4
Visible quotes after scrolling: 40
Reissued request status: 200
Captured/live JSON match: True

These numbers describe this specific target session. A different page, browser version, scroll timing, proxy location, or page response can produce a different request set and file size.

Confirm the Full Sequence in One Script

The separate capture, inspection, and replay programs are easier to understand, but the same stages can be combined:

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}"


# Stage 1: capture. A browser is required.
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("=== capture ===")
print(
    "HAR written:",
    HAR_PATH,
    "-",
    os.path.getsize(HAR_PATH),
    "bytes",
)


# Stage 2: inspect. The browser is closed.
har = json.loads(Path(HAR_PATH).read_text())
entries = har["log"]["entries"]

print("\n=== inspect (no browser) ===")
print("total entries:", 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}")


# Stage 3: reissue one request. The browser remains closed.
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,
            "unexpected status",
            response.headers,
            None,
        )

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

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

print("\n=== reissue (no browser) ===")
print("status:", response.status)
print(
    "first quote author:",
    live_data["quotes"][0]["author"]["name"],
)
print(
    "matches captured body:",
    live_data == captured_data,
)

The combined run printed:

text Copy
=== capture ===
HAR written: session.har - 333259 bytes

=== inspect (no browser) ===
total entries: 10
  application/json: 4
  text/css: 3
  text/html: 1
  application/javascript: 1
  font/woff2: 1

=== reissue (no browser) ===
status: 200
first quote author: Albert Einstein
matches captured body: True

Only the first stage imports and uses Playwright. The later stages operate on the file and the selected live HTTP endpoint.

What a HAR Captures

A HAR file represents HTTP transactions recorded by the browser context.

Depending on the recorder and configuration, an entry can contain:

  • Request method and URL
  • Query-string parameters
  • Request headers
  • Request cookies
  • Posted data
  • Response status
  • Response headers
  • Response cookies
  • MIME type
  • Response content
  • Transfer sizes
  • Timing information
  • Redirect details
  • Cache information

With record_har_content="embed", Playwright stores available response content inside the HAR. Without embedded content, the request inventory can still be useful, but the archive may not contain the response body needed for offline parsing or comparison.

What a HAR Does Not Guarantee

A HAR file is a durable record of captured HTTP activity, but it is not a complete record of every browser behavior.

WebSocket Frames Are Not Archived

HAR models request and response transactions. It does not preserve the sequence of messages carried inside an established WebSocket connection.

A page that combines normal HTTP endpoints with a live WebSocket feed needs separate capture mechanisms:

  • HAR for HTTP request and response traffic
  • WebSocket frame listeners for socket messages

The initial connection may involve an HTTP upgrade, but the ongoing frame stream is outside the normal HAR request-response model.

A HAR Is Not a DOM Snapshot

The file does not preserve the final document tree in the way a DOM snapshot would.

A response body may contain the original HTML or JSON, but later JavaScript mutations, element state, rendered layout, and user-visible appearance are separate concerns.

A HAR Is Not a Video

The archive contains no visual timeline of what appeared on the page.

Use Scrapeless session recording when the goal is to review visible browser behavior. Use HAR capture when the goal is to inspect structured HTTP traffic.

Some Content May Be Missing or Encoded

HAR supports optional content fields. A recorder can omit bodies, and binary resources may be encoded.

Before parsing response.content.text, check that:

  • The text field exists.
  • The content type is expected.
  • The encoding field is handled if present.
  • The body has not been omitted by the recording configuration.

When Browser-Free Reissue Works

A captured request can be reissued successfully when the live server still accepts the reconstructed request.

The public /api/quotes endpoint works because it does not depend on an expiring authenticated session.

Reissue becomes more complicated when the original request uses:

  • Session cookies
  • CSRF tokens
  • Short-lived bearer credentials
  • Signed URLs
  • Per-session request signatures
  • State stored only inside the browser
  • Data generated by a previous navigation step
  • A request body whose content changes per session

The HAR may preserve the original credential values, but it cannot extend their validity. Once those values expire, a new browser session may be required to create fresh state.

Replay HAR Data Safely

A HAR file can contain sensitive session data.

Request and response headers may expose:

  • Cookies
  • Authorization headers
  • API keys
  • Session identifiers
  • Internal URLs
  • Personal data returned by an endpoint

Treat HAR files as sensitive artifacts:

  • Do not commit them to a public repository.
  • Remove credentials before sharing them.
  • Restrict access to archived production sessions.
  • Avoid logging complete headers unnecessarily.
  • Store only the captures needed for the debugging or audit task.
  • Delete archives according to the project’s retention requirements.

The archive in this tutorial comes from a public, unauthenticated demonstration page. The same assumptions should not be applied automatically to authenticated applications.

Common Problems

The HAR File Does Not Appear

The most common cause is closing the browser without explicitly closing the context.

Use:

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

Also confirm that the process can write to the directory containing HAR_PATH.

The HAR Contains No Response Body

Confirm that the context uses:

python Copy
record_har_content="embed"

Then inspect whether entry["response"]["content"]["text"] exists. Some resources may be omitted or represented with an encoding.

urllib Rejects a Header Name

Remove any header whose name starts with :. These are HTTP/2 pseudo-headers, not ordinary header fields.

The URL and request method already carry their relevant meaning.

The Reissued Response Is Compressed Unexpectedly

Do not copy a browser’s accept-encoding value blindly unless the replay client supports every advertised content coding.

Let the HTTP client negotiate an encoding it can decode.

The New Response Does Not Match the HAR

A mismatch does not necessarily mean the reconstruction code is wrong.

The server may return changing content, timestamps, randomized fields, geolocation-specific results, or data tied to credentials that are no longer valid.

Compare the fields that are expected to remain stable instead of assuming that every endpoint always returns identical bytes.

Conclusion

Playwright’s record_har_path turns a browser context into a durable HTTP archive.

The workflow has three clear phases:

  1. Connect Playwright to Scrapeless Scraping Browser and capture the page session.
  2. Close the browser and inspect log.entries as ordinary JSON.
  3. Reconstruct an eligible request with urllib and compare its live response with the captured body.

The browser is required only to produce the archive. Once context.close() flushes the HAR, the file can be parsed on a machine with no Playwright installation, browser process, or CDP connection.

That separation makes HAR capture useful for post-session debugging, internal API discovery, network auditing, and request reconstruction. It also keeps the limitations visible: HAR does not preserve WebSocket frames, rendered visual state, or the future validity of captured credentials.

Review the Scraping Browser product page for the browser-session capabilities behind the capture workflow, and check the Scrapeless pricing plans when moving from a demonstration session to a larger capture workload.

The Chrome DevTools Protocol explainer covers the protocol surface underneath the browser connection.

Ready to Archive a Real Browser Session?

Join the Scrapeless community to compare Playwright capture patterns and browser-debugging workflows with other developers: Discord · Telegram.

Sign up at app.scrapeless.com for free Scraping Browser runtime, then adapt the capture, inspection, and request-reconstruction steps to a public page relevant to your project.


FAQ

Q: What is a HAR file?

A HAR file is a JSON archive of HTTP transactions captured during a browser session. Its log.entries array contains one object per recorded request, with nested request, response, timing, and content information.

A HAR file is not a screenshot, page video, or complete DOM snapshot.

Q: How is HAR capture different from live request interception?

HAR capture records the browser context’s HTTP traffic broadly, while live interception observes matching requests as they occur.

Live interception is useful when the endpoint is already known. HAR capture is useful when the important request may be discovered only after the session has ended.

Q: Do you need a browser to read a HAR file?

No. A completed HAR file is an ordinary JSON document that can be read with Python’s json module.

The browser is needed during capture, but not during offline inspection.

Q: Does HAR capture include WebSocket traffic?

HAR capture does not archive the messages exchanged inside an established WebSocket connection.

Use a WebSocket frame listener when the page depends on a live socket feed. HAR can still cover the ordinary HTTP traffic used by the same page.

Q: Is Playwright HAR recording the same as Scrapeless session recording?

No. Playwright HAR recording creates a structured network archive, while Scrapeless session recording creates a visual replay of the rendered browser session.

Use HAR for request and response analysis. Use session recording when visible page behavior is the object of the investigation.

Q: Is the urllib example the same as Playwright’s route_from_har()?

No. route_from_har() serves recorded responses to requests made inside a live Playwright browser context.

The urllib example reads a request from the HAR and submits a new request to the live server without starting a browser.

Q: Why does a HAR contain headers such as :authority and :method?

Those names are HTTP/2 pseudo-header fields used to carry request control data inside the HTTP/2 protocol.

They are not ordinary header fields, so an HTTP/1.1-style client must represent their meaning through the request method and URL instead of copying the colon-prefixed names.

Q: Can every captured request be reissued successfully?

No. A request can be reissued only while the live server accepts the reconstructed method, URL, body, headers, and credentials.

Requests that depend on expired cookies, CSRF tokens, signed URLs, or short-lived bearer credentials may require a new browser session.

Q: Do you need a separate proxy for this workflow?

No separate proxy configuration is required when the Scrapeless Scraping Browser connection already specifies the desired proxy country.

The example sets proxyCountry=US in the CDP connection URL, so the browser session uses that configured egress.

Q: Is it safe to share a HAR file?

A HAR file should be treated as sensitive until its contents have been reviewed and sanitized.

It may contain cookies, authorization headers, API keys, internal endpoints, submitted form data, or private response content. Remove sensitive values before sharing or committing the file.

Q: Does HAR recording work only with Scrapeless Scraping Browser?

No. record_har_path is a Playwright browser-context option and can be used with compatible local or remote browsers.

Scrapeless Scraping Browser supplies the managed remote Chromium environment and proxy configuration used during the capture stage.

At Scrapeless, we only access publicly available data while strictly complying with applicable laws, regulations, and website privacy policies. The content in this blog is for demonstration purposes only and does not involve any illegal or infringing activities. We make no guarantees and disclaim all liability for the use of information from this blog or third-party links. Before engaging in any scraping activities, consult your legal advisor and review the target website's terms of service or obtain the necessary permissions.

Most Popular Articles

Catalogue