Playwright Browser Automation
Overview
Playwright enables browser automation for web testing, screenshots, form filling, and scraping. This skill uses Python with uv for self-contained scripts that require no global installation.
Prerequisites
- Python 3.10+
- uv package manager
- Playwright browser binaries (one-time setup)
Setup (First Time Only)
Claude: Do not run browser installation commands directly. Suggest these commands to the user and let them run manually. This is a one-time setup that downloads ~200MB of browser binaries.
Suggest the user run:
# Install Chromium (recommended, ~200MB)
uv run --with playwright playwright install chromium
# Or install all browsers
uv run --with playwright playwright install
To verify installation:
uv run /path/to/plugins/playwright/scripts/check_setup.py
Quick Start
Take a screenshot of any URL:
uv run /path/to/plugins/playwright/scripts/screenshot.py https://example.com
Output: /tmp/screenshot-{timestamp}.png
Common Patterns
Take a Screenshot
# Default (visible browser)
uv run scripts/screenshot.py https://example.com
# Full page, headless
uv run scripts/screenshot.py https://example.com --full-page --headless
# Custom output path
uv run scripts/screenshot.py https://example.com -o /tmp/my-shot.png
Navigate and Extract Content
# Get page title and URL
uv run scripts/navigate.py https://example.com
# Extract all links as JSON
uv run scripts/navigate.py https://example.com --links
# Get page text content
uv run scripts/navigate.py https://example.com --text
Fill and Submit Forms
uv run scripts/fill_form.py https://example.com/login \
--field "email=test@example.com" \
--field "password=secret123" \
--submit
Execute JavaScript
uv run scripts/evaluate.py https://example.com "document.title"
uv run scripts/evaluate.py https://example.com "document.querySelectorAll('a').length"
Writing Custom Scripts
Save this template to /tmp/my-automation.py:
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.10"
# dependencies = ["playwright==1.56.0"]
# ///
"""Custom Playwright automation script."""
import os
import sys
from playwright.sync_api import sync_playwright
HEADLESS = os.getenv("HEADLESS", "0").lower() in ("1", "true", "yes")
def main():
with sync_playwright() as p:
browser = p.chromium.launch(headless=HEADLESS)
page = browser.new_page()
try:
page.goto("https://example.com")
print(f"Title: {page.title()}")
# Use semantic locators (preferred)
page.get_by_role("button", name="Submit").click()
page.get_by_label("Email").fill("test@example.com")
# Screenshot
page.screenshot(path="/tmp/result.png")
except Exception as e:
page.screenshot(path="/tmp/error.png")
print(f"Error: {e}", file=sys.stderr)
return 1
finally:
browser.close()
return 0
if __name__ == "__main__":
sys.exit(main())
Run with:
uv run /tmp/my-automation.py
Modern Locator API
Prefer semantic locators over CSS selectors:
# PREFERRED: Semantic locators (accessible, stable)
page.get_by_role("button", name="Submit").click()
page.get_by_label("Email").fill("user@example.com")
page.get_by_placeholder("Search...").fill("query")
page.get_by_text("Welcome back").wait_for()
page.get_by_test_id("submit-btn").click()
# AVOID: Raw CSS selectors (fragile)
page.locator("button.btn-primary").click() # Don't use
Combine locators:
# OR: Match either
page.get_by_role("button", name="New").or_(
page.get_by_text("Create")
).click()
# Filter: Narrow down
page.locator("tr").filter(has_text="Active").first.click()
Quick Reference
| Operation | Code |
|-----------|------|
| Navigate | page.goto("https://url") |
| Click | page.get_by_role("button", name="X").click() |
| Fill input | page.get_by_label("Email").fill("value") |
| Get text | page.get_by_role("heading").text_content() |
| Screenshot | page.screenshot(path="/tmp/shot.png") |
| Wait | page.get_by_text("Loaded").wait_for() |
| Evaluate JS | page.evaluate("document.title") |
Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| HEADLESS | Run browser headless | 0 (headed) |
| SLOW_MO | Slow down actions (ms) | 0 |
| VIEWPORT | Browser viewport | 1280x720 |
| TRACE | Enable tracing | 0 (off) |
Example:
HEADLESS=1 SLOW_MO=250 uv run scripts/screenshot.py https://example.com
Tracing for Debugging
Enable tracing to debug complex automations:
context.tracing.start(screenshots=True, snapshots=True, sources=True)
# ... your automation ...
context.tracing.stop(path="/tmp/trace.zip")
View the trace:
uv run --with playwright playwright show-trace /tmp/trace.zip
Troubleshooting
"Browser not found"
Suggest the user install browser binaries (do not run directly):
uv run --with playwright playwright install chromium
"Timeout waiting for element"
Use proper waiting strategies:
# Wait for element to be visible
page.get_by_text("Loaded").wait_for(state="visible")
# Wait for network idle
page.goto(url, wait_until="networkidle")
"Element not interactable"
Ensure element is visible and scroll into view:
element = page.get_by_role("button", name="Submit")
element.scroll_into_view_if_needed()
element.click()
Headless mode issues
Debug with headed mode:
HEADLESS=0 uv run scripts/screenshot.py https://example.com
Container/CI Issues
Use these Chromium flags:
browser = p.chromium.launch(
headless=True,
args=["--disable-dev-shm-usage", "--no-sandbox"]
)
Advanced Usage
For comprehensive documentation, see:
- references/api-reference.md - Full API reference
- references/selectors.md - Selector patterns
- references/custom-scripts.md - Script templates
- references/troubleshooting.md - Common issues