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.1.1.tar.gz (42.7 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.1-py3-none-any.whl (10.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: pwbase-0.1.1.tar.gz
  • Upload date:
  • Size: 42.7 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.1.tar.gz
Algorithm Hash digest
SHA256 b74e036d5c72497c14659c2d8d6eb465dc229aee3a813d24c3aaafa4a702dafe
MD5 629a02ed1604c4777bc0f76950dbc386
BLAKE2b-256 097b2317b51d16ef8ae1525804f7efc848483e1a37ff8fbb3e25089e736a4e4b

See more details on using hashes here.

File details

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

File metadata

  • Download URL: pwbase-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 10.3 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f1ecf2098e69b51d9852e38dba93678d03a28147ac413e5f1cf3517f2e6996d6
MD5 2c621ea2ea3549920168b53585821326
BLAKE2b-256 0b540f3ecb15dc0b0092ee60f3e8d6ec7c2b00493bb0b352fa5870da498f155b

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