Web Scraping 4 min read

Reduce Headless Browser Resource Use with Content Checks

Profile an authorized Playwright fixture with and without image/font blocking. Record page-level CDP metrics and verify that required content still loads.

(Updated: ) • 679 words

Headless browser resource optimization needs a content check beside the measurements. Compare a defined authorized page with and without a specific resource policy, then verify that required content still appears. Blocking scripts can remove the data you meant to collect. A smaller transfer is not an improvement when the extraction task fails.

Measure a declared browser slice

Recorded field Scope What it does not establish
Navigation-to-ready time The declared page readiness interval End-to-end API or browser-startup latency
CDP TaskDuration Chromium’s page task-duration metric Whole-process CPU usage
Runtime heap usedSize Reported JavaScript heap usage Total browser resident memory
Network encodedDataLength Completed requests reported to the page CDP session An invoice or a universal network-cost estimate
Required-content assertion Known fixture text is present Completeness for arbitrary production pages

Use Chromium and its DevTools Protocol consistently. The metrics are operational observations, not a cross-browser benchmark. Run repeated samples with the same browser version, target and readiness condition before describing a distribution.

Keep scripts and required data requests available

The runnable Python example requires Playwright and an installed Chrome channel. It only blocks image and font resources in the treatment run. Its declared localhost fixture exposes #ready with data-ready set after its required data arrives. For a different authorized target, replace the readiness and required-content checks before interpreting its output.

import json
from time import perf_counter
from urllib.parse import urlparse
from playwright.sync_api import sync_playwright

def profile(url, authorized_hosts, block_images_fonts=False):
    if urlparse(url).hostname not in authorized_hosts:
        raise ValueError("Target outside the authorized sample")
    with sync_playwright() as p:
        browser = p.chromium.launch(channel="chrome", headless=True)
        try:
            context = browser.new_context()
            page = context.new_page()
            if block_images_fonts:
                def route_request(route):
                    if route.request.resource_type in {"image", "font"}:
                        route.abort()
                    else:
                        route.continue_()
                page.route("**/*", route_request)
            cdp = context.new_cdp_session(page)
            cdp.send("Network.enable")
            cdp.send("Performance.enable")
            sizes = []
            cdp.on("Network.loadingFinished", lambda e: sizes.append(e["encodedDataLength"]))
            started = perf_counter()
            page.goto(url, wait_until="domcontentloaded", timeout=15000)
            page.wait_for_function(
                "document.querySelector('#ready')?.dataset.ready === '1'", timeout=15000)
            wall = perf_counter() - started
            metrics = {m["name"]: m["value"] for m in cdp.send("Performance.getMetrics")["metrics"]}
            heap = cdp.send("Runtime.getHeapUsage")
            text = page.locator("main").inner_text()
            result = {"browser_version": browser.version,
                "blocked_images_fonts": block_images_fonts,
                "navigation_to_ready_seconds": wall,
                "cdp_task_duration_seconds": metrics.get("TaskDuration"),
                "cdp_js_heap_used_bytes": heap["usedSize"],
                "cdp_received_encoded_bytes": sum(sizes),
                "fixture_content_present": "Required fixture detail" in text}
            context.close()
            return result
        finally:
            browser.close()

if __name__ == "__main__":
    import sys
    target = sys.argv[1]
    allowed = {"127.0.0.1", "localhost"}
    print(json.dumps([profile(target, allowed, False),
                      profile(target, allowed, True)], indent=2))

Build a fixture that can detect a broken policy

Use an owned local page with a required script, a data response, a nonessential image and a visible required paragraph. The reference run and image/font-blocked run must both satisfy the content assertion. A policy that prevents the required script or data from completing should fail the readiness check instead of being celebrated as faster.

The elapsed interval starts immediately before navigation and excludes the initial browser launch. Each profile uses a fresh context, but that does not prove an upstream service cache is cold. The fixture confirms the recording and validation method; it does not justify a general resource-saving percentage for other websites.

Decide whether the browser is necessary

Use the browser-state decision guide to identify work that needs actions or JavaScript readiness. For known-URL text, compare a permitted Reader operation against the same required output. the extraction-service comparison owns supplier choice, while this page owns resource measurement in a browser you operate.

Sources and verification

Tags:

Web Scraping Tutorial Python Node.js Reader API
SearchCans Team

SearchCans Team

SERP API & Reader API Experts

The SearchCans engineering team builds high-performance search APIs serving developers worldwide. We share practical tutorials, best practices, and insights on SERP data, web scraping, RAG pipelines, and AI integration.

Ready to build with SearchCans?

Test SERP API and Reader API with 100 free credits. No credit card required.