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

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.1.0.tar.gz (42.4 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.1.0-py3-none-any.whl (10.1 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for pwbase-0.1.0.tar.gz
Algorithm Hash digest
SHA256 21dd7116b69f058040334457b0522eb32c6c00e9f957381366bbcf2a590be754
MD5 7c8238ea9d21d4e3b63186becb5e558c
BLAKE2b-256 5ccd6aacf8d1cd6e5e75991571a0860ddc1724e4440e66b7f2ad3809e762a410

See more details on using hashes here.

File details

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

File metadata

  • Download URL: pwbase-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 10.1 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.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3088fe329b7a06f8d6050554f63943d49e56965ee157d5dd01fa67731489520b
MD5 1e84a40e699fc0c74676ea2ae492c0a2
BLAKE2b-256 3865698306fc3d52d09809ba1ca3555db9d4a4161a1d5fee91c31cf1d7ed94f0

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