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

Uploaded Python 3

File details

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

File metadata

  • Download URL: pwbase-0.2.1.tar.gz
  • Upload date:
  • Size: 46.3 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.1.tar.gz
Algorithm Hash digest
SHA256 8839674c07e2cb56d4cde0b6a43fb518de270feb51ac23a769d3b942a7bf5c19
MD5 81dae4d66acbee384aeade8bf9b90dfa
BLAKE2b-256 c406574e54e45b42910ce02ba5e1bbdd18e0135ddfde62133fdb859f8b4d21ce

See more details on using hashes here.

File details

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

File metadata

  • Download URL: pwbase-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 12.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.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 23fb1948c82cb32623838da7775939207146edafd73921546a4c7dc78ac44ebe
MD5 3428a3254d2384a391c22501b96f5536
BLAKE2b-256 f5804a3bab8437744d1c01a7d00c1cd04d8963921e7bd5ea9878e6b9807fb77c

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