Skip to main content

A lightweight async Playwright wrapper for Python that supports three browser launch strategies and can intercept authenticated HTTP sessions from live browser traffic.

Project description

pwbase

A lightweight async Playwright wrapper for Python that supports three browser launch strategies and can intercept authenticated HTTP sessions from live browser traffic.

Features

  • Three browser modes: plain Playwright, stealth (bot-detection evasion), and CDP attachment
  • Persistent browser state (cookies + localStorage) via save_state / state_path
  • BrowserSessionExtractor — intercepts JSON responses and converts them into authenticated requests.Session objects
  • Fully async, context-manager-friendly API

Requirements

  • Python 3.12+
  • uv (recommended) or pip

Installation

Available on PyPI.

uv add pwbase
# or
pip install pwbase

Install Playwright browsers after installing the package:

playwright install chromium

Quick Start

import asyncio
from pwbase import Browser, BrowserConfig, BrowserType

async def main():
    async with Browser(BrowserConfig(type=BrowserType.STEALTH)) as browser:
        page = await browser.get_page()
        await page.goto("https://example.com")
        print(await page.title())

asyncio.run(main())

Browser Modes

Mode BrowserType Description
Default DEFAULT Pure Playwright, no extras
Stealth STEALTH Applies playwright-stealth to reduce bot detection signals
CDP CDP Attaches to an existing Chrome instance via Chrome DevTools Protocol

Default

Browser(BrowserConfig(type=BrowserType.DEFAULT))

Stealth

Browser(BrowserConfig(type=BrowserType.STEALTH))

CDP

Start Chrome with remote debugging enabled:

google-chrome --remote-debugging-port=9222

Then attach:

Browser(BrowserConfig(type=BrowserType.CDP, cdp_url="http://localhost:9222"))

Note: headless, state_path, viewport, and related options are ignored in CDP mode. save_state() is not available in CDP mode.

BrowserConfig Reference

@dataclass
class BrowserConfig:
    type: BrowserType = BrowserType.DEFAULT
    headless: bool = True
    state_path: Path | None = None      # Load/save cookies + localStorage
    channel: str = "chrome"             # Browser channel for STEALTH mode
    cdp_url: str = "http://localhost:9222"
    viewport: tuple[int, int] = (1920, 1080)
    user_agent: str = "..."             # Windows Chrome UA by default
    locale: str = "en-US"
    timezone: str = "America/New_York"
    args: list[str] = [                 # Extra Chromium flags
        "--disable-blink-features=AutomationControlled",
        "--no-sandbox",
    ]

Saving and Restoring Browser State

from pathlib import Path
from pwbase import Browser, BrowserConfig, BrowserType

config = BrowserConfig(
    type=BrowserType.STEALTH,
    state_path=Path("state.json"),
)

# First run — log in and save session
async with Browser(config) as browser:
    page = await browser.get_page()
    await page.goto("https://example.com/login")
    # ... perform login ...
    await browser.save_state()

# Subsequent runs — state is restored automatically
async with Browser(config) as browser:
    page = await browser.get_page()
    await page.goto("https://example.com/dashboard")

Session Extraction

BrowserSessionExtractor extends Browser and intercepts JSON responses in real time. Use it to capture authenticated sessions without manually copying cookies or headers.

from pwbase import BrowserSessionExtractor, BrowserConfig, BrowserType

async with BrowserSessionExtractor(BrowserConfig(type=BrowserType.STEALTH)) as browser:
    page = await browser.get_page()
    await browser.start_recording(page)

    await page.goto("https://example.com")
    # Trigger the API call you want to capture, then:

    response = browser.find_response("api/data")
    if response:
        session = browser.to_session(response)
        r = session.get("https://example.com/api/data")
        print(r.json())

API

Method Description
start_recording(page) Begin intercepting JSON responses on page
stop_recording() Stop intercepting; safe to call if never started
find_response(url_contains) Return the most recent captured response matching the substring
find_all_responses(url_contains) Return all captured responses matching the substring
wait_for_response(url_contains, timeout) Poll until a matching response is captured
to_session(response) Build an authenticated requests.Session from a CapturedResponse

CapturedResponse Fields

@dataclass
class CapturedResponse:
    url: str
    method: str
    headers: dict[str, str]           # Response headers
    body: dict | list | None          # Parsed JSON body
    request_headers: dict[str, str]   # Request headers (HTTP/2 pseudo-headers excluded from session)
    request_post_data: str | None
    cookies: list[Cookie]

Manual Lifecycle

If you prefer not to use the context manager:

browser = Browser(BrowserConfig())
await browser.start()
page = await browser.get_page()
# ... do work ...
await browser.stop()

Development

# Install with dev dependencies
uv sync --group dev

# Run tests
uv run pytest

# Run tests with output
uv run pytest -v

Project Structure

src/pwbase/
├── __init__.py                  # Public API surface
├── browser.py                   # Browser — core async Playwright wrapper
├── browser_config.py            # BrowserConfig dataclass
├── browser_type.py              # BrowserType enum
└── browser_session_extractor.py # BrowserSessionExtractor + CapturedResponse
tests/
├── conftest.py                  # Shared async mock fixtures
├── test_browser.py              # Unit tests for Browser (all three modes)
└── test_browser_session_extractor.py

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pwbase-0.2.2.tar.gz (48.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pwbase-0.2.2-py3-none-any.whl (13.5 kB view details)

Uploaded Python 3

File details

Details for the file pwbase-0.2.2.tar.gz.

File metadata

  • Download URL: pwbase-0.2.2.tar.gz
  • Upload date:
  • Size: 48.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for pwbase-0.2.2.tar.gz
Algorithm Hash digest
SHA256 7316d33d33200b80a0f261fbce363ccd35bb3b0224bef0a6c61c7f3771836c7f
MD5 00a9bada20b9fd018d2751e55a0ca011
BLAKE2b-256 0737dd96fd5fb6dd988d9beb51c8b9660735f70fe24b223d4244750dbcf8ee5b

See more details on using hashes here.

File details

Details for the file pwbase-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: pwbase-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 13.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for pwbase-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 0341170fdf1a41796a2c2ef19d537a699ae9828aca80b98bdde8bc4a399df58f
MD5 453d28cb5cc7210a0416dfe2905759f3
BLAKE2b-256 52a20c487e39e1e8fbac54350c04c625701f9e5fcc1e7b826708b06e73cd32bb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page