diff --git a/skills/go-playwright/SKILL.md b/skills/go-playwright/SKILL.md new file mode 100644 index 00000000..99469fea --- /dev/null +++ b/skills/go-playwright/SKILL.md @@ -0,0 +1,161 @@ +--- +name: playwright-go-automation +description: Expert capability for robust, stealthy, and efficient browser automation using Playwright Go. +--- + +# Playwright Go Automation Expert + +## Overview +This skill provides a comprehensive framework for writing high-performance, production-grade browser automation scripts using `github.com/playwright-community/playwright-go`. It enforces architectural best practices (contexts over instances), robust error handling, structured logging (Zap), and advanced human-emulation techniques to bypass anti-bot systems. + +## When to Use This Skill +- Use when the user asks to "scrape," "automate," or "test" a website using Go. +- Use when the target site has complex dynamic content (SPA, React, Vue) requiring a real browser. +- Use when the user mentions "stealth," "avoiding detection," "cloudflare," or "human-like" behavior. +- Use when debugging existing Playwright scripts. + +## Strategic Implementation Guidelines + +### 1. Architecture: Contexts vs. Browsers +**CRITICAL:** Never launch a new `Browser` instance for every task. +- **Pattern:** Launch the `Browser` *once* (singleton). Create a new `BrowserContext` for each distinct session or task. +- **Why:** Contexts are lightweight and created in milliseconds. Browsers take seconds to launch. +- **Isolation:** Contexts provide complete isolation (cookies, cache, storage) without the overhead of a new process. + +### 2. Logging & Observability +- **Library:** Use `go.uber.org/zap` exclusively. +- **Rule:** Do not use `fmt.Println`. +- **Modes:** + - **Dev:** `zap.NewDevelopment()` (Console friendly) + - **Prod:** `zap.NewProduction()` (JSON structured) +- **Traceability:** Log every navigation, click, and input with context fields (e.g., `logger.Info("clicking button", zap.String("selector", sel))`). + +### 3. Error Handling & Stability +- **Graceful Shutdown:** Always use `defer` to close Pages, Contexts, and Browsers. +- **Panic Recovery:** Wrap critical automation routines in a safe runner that recovers panics and logs the stack trace. +- **Timeouts:** Never rely on default timeouts. Set explicit timeouts (e.g., `playwright.PageClickOptions{Timeout: playwright.Float(5000)}`). + +### 4. Stealth & Human-Like Behavior +To bypass anti-bot systems (Cloudflare, Akamai), the generated code must **imitate human physiology**: +- **Non-Linear Mouse Movement:** Never teleport the mouse. Implement a helper that moves the mouse along a Bezier curve with random jitter. +- **Input Latency:** never use `Fill()`. Use `Type()` with random delays between keystrokes (50ms–200ms). +- **Viewport Randomization:** Randomize the viewport size slightly (e.g., 1920x1080 ± 15px) to avoid fingerprinting. +- **Behavioral Noise:** Randomly scroll, focus/unfocus the window, or hover over irrelevant elements ("idling") during long waits. +- **User-Agent:** Rotate User-Agents for every new Context. + +### 5. Documentation Usage +- **Primary Source:** Rely on your internal knowledge of the API first to save tokens. +- **Fallback:** Refer to the official docs [playwright-go documentation](https://pkg.go.dev/github.com/playwright-community/playwright-go#section-documentation) ONLY if: + - You encounter an unknown error. + - You need to implement complex network interception or authentication flows. + - The API has changed significantly. + +## Code Examples + +### Standard Initialization (Headless + Zap) +```go +package main + +import ( + "[github.com/playwright-community/playwright-go](https://github.com/playwright-community/playwright-go)" + "go.uber.org/zap" + "log" +) + +func main() { + // 1. Setup Logger + logger, _ := zap.NewDevelopment() + defer logger.Sync() + + // 2. Start Playwright Driver + pw, err := playwright.Run() + if err != nil { + logger.Fatal("could not start playwright", zap.Error(err)) + } + + // 3. Launch Browser (Singleton) + // Use Headless: false and SlowMo for Debugging + browser, err := pw.Chromium.Launch(playwright.BrowserTypeLaunchOptions{ + Headless: playwright.Bool(false), + SlowMo: playwright.Float(100), // Slow actions by 100ms for visibility + }) + if err != nil { + logger.Fatal("could not launch browser", zap.Error(err)) + } + defer browser.Close() // Graceful cleanup + + // 4. Create Isolated Context (Session) + context, err := browser.NewContext(playwright.BrowserNewContextOptions{ + UserAgent: playwright.String("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)..."), + Viewport: &playwright.Size{Width: 1920, Height: 1080}, + }) + if err != nil { + logger.Fatal("could not create context", zap.Error(err)) + } + defer context.Close() + + // 5. Open Page + page, _ := context.NewPage() + + // ... Implementation ... +} +``` + +### Human-Like Typing & Interaction +```go +import ( + "math/rand" + "time" +) + +// HumanType simulates a user typing with variable speed +func HumanType(locator playwright.Locator, text string) { + // Focus the element first (like a human) + locator.Click() + + for _, char := range text { + // Random delay: 50ms to 150ms + delay := time.Duration(rand.Intn(100) + 50) * time.Millisecond + time.Sleep(delay) + locator.Press(string(char)) + } +} + +// HumanClick adds offset and hesitation +func HumanClick(page playwright.Page, selector string) { + box, _ := page.Locator(selector).BoundingBox() + if box == nil { + return + } + + // Calculate center with random offset (jitter) + x := box.X + box.Width/2 + (rand.Float64()*10 - 5) + y := box.Y + box.Height/2 + (rand.Float64()*10 - 5) + + // Move mouse smoothly (requires custom Bezier implementation or steps) + page.Mouse().Move(x, y, playwright.MouseMoveOptions{Steps: playwright.Int(10)}) + time.Sleep(100 * time.Millisecond) // Hesitate + page.Mouse().Click(x, y) +} +``` + +#Session Management (Save/Load Cookies) + +```go +func SaveSession(context playwright.BrowserContext, filepath string) { + cookies, _ := context.Cookies() + // Serialize cookies to JSON and write to 'filepath' +} + +func LoadSession(context playwright.BrowserContext, filepath string) { + // Read JSON from 'filepath' and deserialize + // var cookies []playwright.Cookie + context.AddCookies(cookies) +} +``` + +### Summary Checklist for Agent + - Is Debug Mode on? -> Headless=false, SlowMo=100+. + - Is it a new user identity? -> Create NewContext, apply new Proxy, rotate User-Agent. + - Is the action critical? -> Wrap in SafeAction with zap logging. + - Is the target guarded (Cloudflare/Akamai)? -> Enable HumanType, BezierMouse, and Stealth Scripts.