Skip to content

Instantly share code, notes, and snippets.

@lmmendes
Created February 17, 2026 17:16
Show Gist options
  • Select an option

  • Save lmmendes/890148c01e22778252cf0164bf770e18 to your computer and use it in GitHub Desktop.

Select an option

Save lmmendes/890148c01e22778252cf0164bf770e18 to your computer and use it in GitHub Desktop.
browser-automation skill for nanobot (https://github.com/HKUDS/nanobot) using uv and playwrite
name description
browser-automation
Browser automation via Playwright. Navigate pages, click elements, fill forms, take screenshots, and extract data from websites. Use this skill whenever the user asks to interact with a website, scrape data, fill a form, take a screenshot, or automate any browser task.

Browser Automation (Playwright + uv)

This skill provides browser automation using Playwright, executed via uv. All browser commands are executed through the exec (shell) tool.

This skill must never use pip install and must never modify system Python. All Python execution MUST go through uv.


Execution Model

Playwright availability follows this strict priority order:

  1. Playwright installed as a user-wide uv tool (preferred)
  2. Ephemeral uv environment via uv run --with playwright (fallback)

The skill MUST always attempt option 1 before option 2.


IMPORTANT: First-Run Setup (per session)

Before running ANY Playwright command, you MUST verify that Playwright is available.

Step 1 — Check for Playwright CLI (uv tool)

playwright --version

If this succeeds, Playwright is already available. Proceed directly to Usage.


Step 2 — Install Playwright as a uv tool (preferred)

If the command is not found, install Playwright as a user-wide uv tool:

uv tool install playwright
playwright install chromium

If Chromium installation fails due to missing system dependencies on Linux:

playwright install-deps chromium
playwright install chromium

Once this completes successfully, confirm to the user:

Browser automation is now ready (Playwright installed via uv tool).


Step 3 — Fallback: Ephemeral uv environment

If installing a user-wide tool is not possible, fall back to an isolated uv-managed environment:

uv run --with playwright playwright install chromium

Confirm to the user:

Browser automation is now ready (Playwright via uv run).


Usage

Preferred execution path (Playwright uv tool installed)

uv run python -c "
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto('https://example.com')
    print(page.title())
    browser.close()
"

Fallback execution path (no uv tool)

uv run --with playwright python -c "
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto('https://example.com')
    print(page.title())
    browser.close()
"

Common Tasks

Navigate and get page content

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto(URL, timeout=15000)

    print(page.content())
    print(page.title())
    print(page.inner_text("body"))

    browser.close()

Take a screenshot

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto(URL)

    page.screenshot(path="/tmp/screenshot.png", full_page=True)

    browser.close()

Fill a form and submit

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto(URL)

    page.fill('input[name="username"]', 'myuser')
    page.fill('input[name="password"]', 'mypass')
    page.click('button[type="submit"]')

    page.wait_for_load_state("networkidle")
    print(page.title())

    browser.close()

Click and interact with elements

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto(URL)

    page.click("text=Sign In")
    page.click("#submit-button")
    page.select_option("select#country", "US")

    page.keyboard.type("search query")
    page.keyboard.press("Enter")

    browser.close()

Wait for dynamic content

page.wait_for_selector(".results-loaded", timeout=10000)
page.wait_for_load_state("networkidle")
page.wait_for_url("**/dashboard")

Extract structured data

from playwright.sync_api import sync_playwright
import json

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto(URL)

    items = page.query_selector_all(".product-card")
    results = []

    for item in items:
        results.append({
            "name": item.query_selector(".name").inner_text(),
            "price": item.query_selector(".price").inner_text(),
        })

    print(json.dumps(results, indent=2))
    browser.close()

Workflow Pattern

For any browser automation task:

  1. Check Playwright CLI availability
  2. Install via uv tool if missing
  3. Fallback to uv run --with playwright if needed
  4. Write a Python script if the task is multi-step
  5. Execute it via uv
  6. Return results (text, data, file paths)

Hard Rules

  • ❌ Never run pip install
  • ❌ Never modify system Python
  • ❌ Never assume Playwright is preinstalled
  • ❌ Never skip browser installation
  • ✅ Always use uv
  • ✅ Always close browsers properly

Tips

  • Use headless=True unless explicitly requested otherwise
  • Set navigation timeouts: page.goto(url, timeout=15000)
  • Use page.wait_for_load_state("networkidle") for JS-heavy pages
  • Store artifacts in /tmp or workspace directories
  • Prefer with sync_playwright() for guaranteed cleanup
  • page.wait_for_timeout(2000) is acceptable as a last-resort fallback
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment