agentsclimarketplace

Playwright automation

Skill bdmorin/.claude/skills/playwright-automation

Execute complex browser automation using Playwright Python. Use for video recording, multi-page navigation, data extraction. Triggers on "browser script", "record video of website", "extract data from webpage".From its SKILL.md

Install
npx -y skills add bdmorin/.claude --skill playwright-automation

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

7.0 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it

Playwright Automation Skill

Python-based browser automation for complex workflows requiring video recording, multi-page navigation, or data extraction.

When to Use

  • Video recording of browser interactions
  • Multi-page navigation sequences
  • Data extraction with post-processing
  • Form filling with validation
  • Screenshot sequences or comparisons
  • Any automation requiring Python logic

Output Directories

/workspace/.claude/.data/playwright/
├── screencaps/    # Screenshots (.png, .jpg)
├── videos/        # Recordings (.webm)
├── pdfs/          # Generated PDFs
├── traces/        # Debug traces (.zip)
└── data/          # Extracted data (.json, .csv)

Wait Strategy Guide

StrategyUse CaseSpeedReliability
domcontentloadedGeneral web pages (default)Fast (1-3s)High
loadMedia-heavy sites, SPAsMedium (2-5s)High
networkidleStatic sites that fully settleSlow (5-30s+)Variable

Recommendation: Use domcontentloaded (default) for most sites. Only use networkidle for static pages where you need all resources loaded.

Utility Scripts

Screenshot Utility

cd /workspace/.claude/skills/playwright-automation && \
uv run python scripts/screenshot.py <url> \
  [--full-page] \
  [--wait-until domcontentloaded|load|networkidle] \
  [--timeout 30000] \
  [--output filename.png]

Video Recorder

cd /workspace/.claude/skills/playwright-automation && \
uv run python scripts/video_recorder.py <url> \
  [--duration 10] \
  [--wait-until domcontentloaded|load|networkidle] \
  [--timeout 30000] \
  [--output filename.webm]

Script Template

Use this template for custom automation scripts:

#!/usr/bin/env python3
"""
Browser automation script: [DESCRIPTION]
Output: /workspace/.claude/.data/playwright/[TYPE]/[FILENAME]
"""
import os
from datetime import datetime
from loguru import logger
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError

SCREENCAP_DIR = "/workspace/.claude/.data/playwright/screencaps"
VIDEO_DIR = "/workspace/.claude/.data/playwright/videos"
DATA_DIR = "/workspace/.claude/.data/playwright/data"

os.makedirs(SCREENCAP_DIR, exist_ok=True)
os.makedirs(VIDEO_DIR, exist_ok=True)
os.makedirs(DATA_DIR, exist_ok=True)

def main():
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")

    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        context = browser.new_context(
            viewport={"width": 1920, "height": 1080},
            record_video_dir=VIDEO_DIR,
            record_video_size={"width": 1280, "height": 720}
        )
        page = context.new_page()

        try:
            # Navigate with fallback
            page.goto("https://example.com", wait_until="domcontentloaded", timeout=30000)
            page.wait_for_timeout(2000)  # Allow dynamic content to load

            # Your automation code here
            page.screenshot(path=f"{SCREENCAP_DIR}/example_{timestamp}.png", full_page=True)

        except PlaywrightTimeoutError:
            logger.error("Navigation timeout")
            raise
        finally:
            # Get video path BEFORE closing page
            video = page.video
            video_path = video.path() if video else None

            page.close()
            if video_path:
                print(f"Video saved: {video_path}")
            context.close()
            browser.close()

if __name__ == "__main__":
    main()

Common Patterns

Screenshot Capture

SCREENCAP_DIR = "/workspace/.claude/.data/playwright/screencaps"

# Full page
page.screenshot(path=f"{SCREENCAP_DIR}/full.png", full_page=True)

# Viewport only
page.screenshot(path=f"{SCREENCAP_DIR}/viewport.png")

# Specific element
page.locator("#element").screenshot(path=f"{SCREENCAP_DIR}/element.png")

# With options
page.screenshot(
    path=f"{SCREENCAP_DIR}/custom.jpg",
    type="jpeg",
    quality=80,
    clip={"x": 0, "y": 0, "width": 800, "height": 600}
)

Video Recording

VIDEO_DIR = "/workspace/.claude/.data/playwright/videos"

# Enable in context creation
context = browser.new_context(
    record_video_dir=VIDEO_DIR,
    record_video_size={"width": 1280, "height": 720}
)
page = context.new_page()
# ... perform actions ...

# CRITICAL: Get path BEFORE closing
video = page.video
video_path = video.path() if video else None
page.close()  # Video finalized here

Form Interaction

page.fill("#username", "testuser")
page.fill("#password", "testpass")
page.click("button[type='submit']")
page.select_option("#country", "US")
page.check("#agree-terms")
page.set_input_files("#file-upload", "/path/to/file.pdf")

Data Extraction

import json

DATA_DIR = "/workspace/.claude/.data/playwright/data"

title = page.locator("h1").text_content()
items = page.locator(".item").all()
data = [item.text_content() for item in items]

with open(f"{DATA_DIR}/extracted.json", "w") as f:
    json.dump({"title": title, "items": data}, f, indent=2)

Wait Strategies

# Wait for specific element
page.wait_for_selector("#dynamic-content")

# Wait for URL change
page.wait_for_url("**/success")

# Wait for load state (use sparingly)
page.wait_for_load_state("networkidle")

# Custom timeout
page.wait_for_selector("#slow-element", timeout=60000)

# Fixed delay for dynamic content
page.wait_for_timeout(2000)

Error Handling with Fallback

from playwright.sync_api import TimeoutError as PlaywrightTimeoutError

try:
    page.goto(url, wait_until="networkidle", timeout=30000)
except PlaywrightTimeoutError:
    # Fallback to faster strategy
    page.goto(url, wait_until="domcontentloaded", timeout=30000)

Tracing for Debug

DATA_DIR = "/workspace/.claude/.data/playwright/data"

context.tracing.start(screenshots=True, snapshots=True, sources=True)
# ... perform actions ...
context.tracing.stop(path=f"{DATA_DIR}/trace.zip")
# View with: playwright show-trace trace.zip

Execution

Run scripts using uv from the skill directory:

cd /workspace/.claude/skills/playwright-automation && uv run python scripts/<script>.py

Or for custom scripts in other locations:

cd /workspace/.claude/skills/playwright-automation && uv run python /path/to/custom_script.py

Troubleshooting

IssueSolution
Browser launch failsEnsure headless=True in container
Element not foundUse wait_for_selector() before interaction
Video not savedGet page.video.path() BEFORE page.close()
Permission deniedCheck output directory permissions
Timeout errorsUse domcontentloaded instead of networkidle
Streaming sites timeoutAdd page.wait_for_timeout(3000) after navigation

What ships with it: 4 files

16.2 KB alongside SKILL.md, 3 of them executable

scripts/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.