# Playwright Scraper

> Build a Playwright walkthrough script that signs into a web app, walks every step, fills forms with sensible defaults, answers Yes/No wizards, takes a numbered screenshot at each step, and produces an HTML gallery + composite PNG — and, for full-app captures, an interactive sitemap graph (one thumbnail node per screen, edges = real navigation) plus a grouped multi-flow gallery with an optional language toggle. Use when the user asks to "walk through" a URL with Playwright, "screenshot every step" of an app, "scrape" a Lovable/v0/Bolt/Streamlit prototype, generate UI documentation or a visual sitemap, capture a flow for design review, or build a regression baseline of an app's UI.

- Skill: `chakshugautam/playwright-scraper` (Agent Skill)
- Install (CLI): `npx skillmds@latest add chakshugautam/playwright-scraper`
- Raw SKILL.md: https://api.skillmd.com/api/skills/chakshugautam/playwright-scraper/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: chakshugautam (https://skillmd.com/u/chakshugautam)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/chakshugautam/playwright-scraper

---


# Playwright walkthrough builder

You are about to write a Playwright script that walks through a web app the user gave you and produces a labeled screenshot gallery.

This skill ships with a helper library at `playwright_scraper` (see the repo at https://github.com/ChakshuGautam/playwright-scraper). Prefer using it over re-implementing — the helpers were tuned against real prototypes (Lovable apps in particular) and handle the edge cases listed below.

## Required output

Always produce **three artifacts** in an output directory:

1. **Numbered screenshots** — `01_signin_blank.png`, `02_signin_filled.png`, ... one per step
2. **`index.html`** — a click-to-zoom gallery
3. **`_graph_all.png`** — a single composite PNG of every screenshot

The user wants to *see* the flow without re-running anything, so the gallery is the deliverable, not the script.

## How to write the script

```python
import asyncio
from playwright.async_api import async_playwright
from playwright_scraper import Walker

OUT = "output"
URL = "https://example.com/"

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        ctx = await browser.new_context(viewport={"width": 1440, "height": 900})
        page = await ctx.new_page()
        page.on("pageerror", lambda exc: print(f"[pageerror] {exc}"))

        w = Walker(page, OUT)

        # 1. Initial load
        await page.goto(URL, wait_until="networkidle", timeout=60000)
        await page.wait_for_timeout(1500)
        await w.shot("landing")

        # 2. Auth (if any) — fill explicitly, never guess passwords with smart_fill
        # ...

        # 3. Generic walk through the rest of the flow
        previous_text = ""
        for i in range(20):
            await w.smart_fill()              # text inputs
            await w.pick_first_radio_in_each_group()
            await w.check_consents()
            await w.answer_yesno("No")        # binary wizard questions
            await w.shot(f"step_{i:02d}_filled")

            clicked = await w.click_forward()
            if not clicked:
                await w.shot(f"step_{i:02d}_final")
                break
            await page.wait_for_timeout(2500)
            await w.shot(f"step_{i:02d}_after_{clicked.lower().replace(' ', '_')}")

            cur_text = (await w.page_text())[:500]
            if cur_text == previous_text:
                break
            previous_text = cur_text

        w.build_gallery(title="example.com walkthrough")
        await browser.close()

asyncio.run(main())
```

## Patterns the helpers already know

**`Walker.shot(label)`** — saves `NN_<label>.png` and a sibling `.txt` with the page's innerText (extremely useful for debugging: when something goes wrong, read the `.txt` rather than re-running).

**`Walker.smart_fill()`** — fills every empty visible `<input>` / `<textarea>` with a sensible default based on its `placeholder` / `name` / `id` / `aria-label`. Knows about: email, phone, name (first/last/full/business), address, city/state/country, PIN/GST/PAN/Aadhaar, dept, fee, SLA, description, code/slug, and more. Pass `overrides={"workspace": "GOI"}` to override values by hint-substring match.

**`Walker.answer_yesno(choice="No")`** — finds every Yes/No button pair on the page (grouped by parent element) and clicks the chosen side. Wizards with multiple questions per step need ALL answered before "Continue" enables.

**`Walker.click_forward()`** — tries every reasonable forward-progress label: Continue, Next, Submit, Save and continue, Confirm, Finish, Done, Publish, Launch, Create, Apply, Start Application, etc. Returns the label clicked or `None`.

**`Walker.build_gallery(title=...)`** — emits `index.html` and `_graph_all.png` from the saved screenshots.

## Scaling up: full-app captures with a sitemap graph

A single `Walker` script covers one linear flow. When the user wants the *whole app* (every route, wizard branches, edge states, maybe multiple languages), switch to the multi-flow layout and the three renderers in the library — don't hand-roll HTML:

1. **One capture script per flow**, writing to `output/<lang>/<NN_flow>/` (e.g. `output/pt/05_auth/`). Single-language apps still benefit from the per-flow folders.
2. **`collect_node_assets(out_dir, flows, classify, langs=..., captions=...)`** — maps every screenshot to a *screen* (sitemap node). You supply `classify(flow, filename) -> node_id`; prefer stable filename markers (`step1`, `step2`, ...) over keywords when writing shot labels, it makes the classifier trivial.
3. **`build_sitemap(out_dir, NODES, EDGES, group_colors=..., langs=..., links=...)`** — renders the interactive vis-network canvas: one thumbnail node per screen, edges = the real navigation graph, click-to-lightbox with zoom/pan, language toggle. `NODES` is `[(id, label, level, group), ...]` where `level` is the vertical rank; `extra_nodes=` merges late additions (gap states, back-office clusters) without touching the canonical model.
4. **`build_flow_gallery(out_dir, [(flow_dir, title), ...], langs=...)`** — the grouped grid gallery (`gallery.html`) with a sticky flow nav; link it and the sitemap to each other via `links=`.

The complete reference configuration is `examples/recla_denuncia_full/` in the repo: bilingual PT/EN capture, 17 flows, gap/edge coverage, per-language URLs (`/pt/`, `/en/`) built by re-running the builders with `ASSET_PREFIX=/ DEFAULT_LANG=<lang> OUT_FILE=<lang>/index.html`, and a deploy script that rsyncs `output/` to nginx and builds downloadable zips.

Make the sitemap graph the landing page (`index.html`) for full-app captures — it answers "what did you cover?" at a glance in a way the flat grid can't.

## Pitfalls to avoid (learned the hard way)

1. **Don't smart-fill password fields.** `smart_fill` already skips them. For sign-in, fill `#password` (or whatever selector) directly with the user-provided value. For "reset password" steps, ALSO fill explicitly — using a generic default will fail validation with "Current password is incorrect."

2. **Wizards with many Y/N questions per step.** A single step may have 3 separate questions; "Continue" stays disabled until *all* are answered. Call `answer_yesno` and check it returned > 0 — if so, look at the page again, more might have appeared. Repeat up to a few times.

3. **State-machine wizards keep the same URL.** Many SPA wizards mutate state without changing the URL. Don't rely on URL change to detect "we moved forward" — compare page text (`page_text()` first 300-500 chars) before vs after.

4. **Look at the page after the first failed click_forward.** When forward returns None, the screenshot of the current state will tell you why (a Yes/No you missed, an unchecked consent, an invalid email). Don't just retry — look.

5. **Phone-frame previews are NOT mobile viewports.** Lovable-style apps often embed a citizen view inside a phone-mockup at desktop viewport. Don't switch to a mobile device profile — the app already renders both frames inside one desktop page. Look for desktop-frame toggle buttons (icons named monitor/laptop) to switch.

6. **Common forward labels you'll miss without click_forward:** "Start Application", "Use template", "Go Live", "Publish", "Choose Template". The helper's default list includes these.

7. **Tab clicks via `:text-is('Foo')` can accidentally match anything containing 'Foo'.** Use `get_by_role("tab", name="Foo")` first; fall back to `:text-is`. Some UI library "tabs" are really `<button>` — try both.

8. **The .txt sidecar from `shot()` is your friend.** Reading the rendered body text after each step is faster than re-running with `--headed`. When the script hits a wall, `grep -l 'Continue' output/*.txt` shows which steps still had a Continue button.

9. **Avoid mocking the auth.** If sign-in is "anything@anything + temp password", just fill realistic values rather than trying to bypass.

10. **Run with `headless=True` by default.** The user almost always wants the gallery, not to watch the browser. Only flip headed if you're actively debugging.

## Workflow when the user gives you a URL

1. **Reconnaissance pass first.** Write a 20-line script that just loads the page and dumps controls (inputs + buttons + their text/placeholders). Read that output before writing the full walkthrough.
2. **Identify auth.** Ask the user for credentials if they aren't obvious. For prototypes ("temp password 12345678" is common), try the password they gave with a placeholder email like `admin@organization.gov`.
3. **Write the walkthrough.** Hand-code the first 2-3 known steps (auth + first nav), then loop with `smart_fill` + `click_forward` for the rest.
4. **Run, inspect the gallery, fix gaps.** If the script gets stuck at step N, open `NN_*.txt`, figure out what the page wants, adjust, re-run.
5. **Tour every sidebar/nav route too.** Once authenticated, iterate over every link in the navigation and screenshot each page — this catches hidden features the linear walkthrough misses.
6. **Build the gallery** and tell the user where to view it.

## What the deliverable looks like

The user gets a folder they can `cd` into and open `index.html`. Each screenshot has a one-line caption derived from the filename. The composite PNG lets them see the whole flow on one screen.

If the user also has a static hosting setup (e.g. an nginx wildcard like `*.proto.example.com`), offer to drop the folder into `/var/www/<slug>/`, add an nginx block, and provision a Let's Encrypt cert. The gallery is just static HTML+PNG.

