{"skill":{"slug":"skylv-web-automation-bot","displayName":"Skylv Web Automation Bot","summary":"AI-driven browser automation for web scraping, form filling, and UI interactions using Playwright or Puppeteer libraries.","description":"---\nname: \"browser-automation-agent\"\nslug: skylv-browser-automation-agent\nversion: 1.0.2\ndescription: Browser automation Agent. Web scraping, form filling, and UI automation using Playwright or Puppeteer. Triggers: browser automation, web scraping, playwright, puppeteer.\nauthor: SKY-lv\nlicense: MIT-0\ntags: [browser, openclaw, agent]\nkeywords: browser, automation, playwright, puppeteer, scraping\ntriggers: browser automation agent\n---\n\n# Browser Automation Agent\n\n## 功能说明\n\nAI驱动的浏览器自动化，执行复杂网页任务。\n\n## 技术选型\n\n| 库 | 特点 | 适用场景 |\n|----|------|----------|\n| Playwright | 跨浏览器、等待稳定 | 通用自动化 |\n| Puppeteer | Chrome专用、Node原生 | Chrome深度控制 |\n| Selenium | 多语言、老牌稳定 | 多浏览器兼容 |\n| DrissionPage | Python、轻量 | 中国网站适配 |\n\n## Playwright 完整实现\n\n### 1. 基础框架\n\n```python\nfrom playwright.sync_api import sync_playwright, Page, Browser, BrowserContext\nfrom dataclasses import dataclass\nfrom typing import Optional\nimport json\n\n@dataclass\nclass TaskResult:\n    success: bool\n    data: Optional[dict] = None\n    error: Optional[str] = None\n    screenshot: Optional[str] = None\n\nclass BrowserAgent:\n    def __init__(self, headless: bool = True, slow_mo: int = 100):\n        self.playwright = sync_playwright().start()\n        self.browser: Browser = self.playwright.chromium.launch(\n            headless=headless,\n            slow_mo=slow_mo\n        )\n        self.context: BrowserContext = self.browser.new_context(\n            viewport={\"width\": 1920, \"height\": 1080},\n            user_agent=\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36\"\n        )\n    \n    def new_page(self) -> Page:\n        return self.context.new_page()\n    \n    def execute(self, task: str) -> TaskResult:\n        \"\"\"执行AI指令\"\"\"\n        page = self.new_page()\n        try:\n            # AI解析任务\n            steps = self.plan_steps(task)\n            \n            for step in steps:\n                self.execute_step(page, step)\n            \n            return TaskResult(success=True, data={\"steps_completed\": len(steps)})\n        except Exception as e:\n            return TaskResult(success=False, error=str(e))\n        finally:\n            page.close()\n    \n    def plan_steps(self, task: str) -> list[dict]:\n        \"\"\"AI规划步骤\"\"\"\n        # 这里集成LLM进行任务规划\n        # ...\n        pass\n    \n    def execute_step(self, page: Page, step: dict):\n        action = step[\"action\"]\n        \n        if action == \"goto\":\n            page.goto(step[\"url\"], wait_until=\"domcontentloaded\")\n        elif action == \"click\":\n            page.click(step[\"selector\"])\n        elif action == \"type\":\n            page.fill(step[\"selector\"], step[\"text\"])\n        elif action == \"wait\":\n            page.wait_for_timeout(step[\"ms\"])\n        elif action == \"screenshot\":\n            page.screenshot(path=step[\"path\"])\n        elif action == \"extract\":\n            return page.query_selector(step[\"selector\"]).inner_text()\n    \n    def close(self):\n        self.browser.close()\n        self.playwright.stop()\n```\n\n### 2. 智能元素定位\n\n```python\nclass SmartLocator:\n    \"\"\"AI智能定位器\"\"\"\n    \n    LOCATOR_STRATEGIES = [\n        \"get_by_role\",      # 无障碍角色（最可靠）\n        \"get_by_label\",     # 表单标签\n        \"get_by_placeholder\",  # 占位符\n        \"get_by_text\",      # 文本内容\n        \"locator\",          # CSS选择器\n        \"xpath\",             # XPath（最后备选）\n    ]\n    \n    def find(self, page: Page, description: str) -> Locator:\n        \"\"\"根据描述智能查找元素\"\"\"\n        strategies = [\n            # 按钮查找\n            lambda: page.get_by_role(\"button\", name=description),\n            lambda: page.get_by_role(\"link\", name=description),\n            \n            # 输入框查找\n            lambda: page.get_by_label(description),\n            lambda: page.get_by_placeholder(description),\n            \n            # 文本查找\n            lambda: page.get_by_text(description, exact=True),\n            lambda: page.get_by_text(description),\n            \n            # 通配符（放在最后）\n            lambda: page.locator(f\"text={description}\").first,\n        ]\n        \n        for strategy in strategies:\n            try:\n                locator = strategy()\n                if locator.count() > 0:\n                    return locator.first\n            except:\n                continue\n        \n        raise ElementNotFound(f\"找不到元素: {description}\")\n    \n    def find_many(self, page: Page, description: str) -> list:\n        \"\"\"批量查找元素\"\"\"\n        # 尝试多种策略\n        # ...\n        pass\n```\n\n### 3. 表单自动填写\n\n```python\nclass FormFiller:\n    \"\"\"智能表单填写\"\"\"\n    \n    def fill_form(self, page: Page, form_data: dict):\n        \"\"\"根据字段描述自动填写\"\"\"\n        for field_name, value in form_data.items():\n            try:\n                # 尝试找对应label的输入框\n                locator = page.get_by_label(field_name, exact=False)\n                \n                # 获取输入类型\n                if locator.count() == 0:\n                    # 尝试placeholder\n                    locator = page.get_by_placeholder(field_name)\n                \n                if locator.count() == 0:\n                    # 尝试名称属性\n                    locator = page.locator(f'[name=\"{field_name}\"]')\n                \n                if locator.count() > 0:\n                    el = locator.first\n                    tag = el.evaluate(\"el => el.tagName\")\n                    \n                    if tag == \"SELECT\":\n                        el.select_option(value)\n                    elif tag == \"INPUT\":\n                        input_type = el.get_attribute(\"type\")\n                        if input_type in [\"checkbox\", \"radio\"]:\n                            if value:\n                                el.check()\n                        else:\n                            el.fill(str(value))\n                    else:\n                        el.fill(str(value))\n                        \n            except Exception as e:\n                print(f\"填写字段 {field_name} 失败: {e}\")\n    \n    def extract_form(self, page: Page, form_selector: str = \"form\") -> dict:\n        \"\"\"提取表单数据\"\"\"\n        form = page.locator(form_selector).first\n        data = {}\n        \n        inputs = form.locator(\"input, select, textarea\")\n        for inp in inputs.all():\n            name = inp.get_attribute(\"name\") or inp.get_attribute(\"id\")\n            if not name: continue\n            \n            input_type = inp.get_attribute(\"type\") or \"text\"\n            if input_type in [\"submit\", \"button\", \"reset\", \"image\"]: continue\n            \n            if input_type == \"checkbox\":\n                data[name] = inp.is_checked()\n            elif input_type == \"radio\":\n                checked = form.locator(f'input[name=\"{name}\"]:checked')\n                data[name] = checked.get_attribute(\"value\") if checked.count() > 0 else None\n            else:\n                data[name] = inp.input_value()\n        \n        return data\n```\n\n### 4. 数据采集\n\n```python\nclass WebScraper:\n    \"\"\"网页数据采集\"\"\"\n    \n    def scrape_table(self, page: Page, table_selector: str) -> list[dict]:\n        \"\"\"采集表格数据\"\"\"\n        table = page.locator(table_selector).first\n        \n        # 获取表头\n        headers = table.locator(\"thead th, th\").all_text_contents()\n        \n        # 采集每行\n        rows = []\n        for row in table.locator(\"tbody tr, tr\").all():\n            cells = row.locator(\"td, th\").all_text_contents()\n            if len(cells) == len(headers):\n                rows.append(dict(zip(headers, cells)))\n        \n        return rows\n    \n    def scrape_cards(self, page: Page, card_selector: str, fields: dict) -> list[dict]:\n        \"\"\"采集卡片式列表\"\"\"\n        cards = page.locator(card_selector).all()\n        results = []\n        \n        for card in cards:\n            item = {}\n            for field_name, selector in fields.items():\n                try:\n                    el = card.locator(selector).first\n                    item[field_name] = el.inner_text().strip()\n                except:\n                    item[field_name] = None\n            results.append(item)\n        \n        return results\n    \n    def scroll_scrape(self, page: Page, item_selector: str, max_items: int = 100) -> list:\n        \"\"\"滚动加载采集\"\"\"\n        items = []\n        last_count = 0\n        \n        while len(items) < max_items:\n            # 滚动\n            page.evaluate(\"window.scrollTo(0, document.body.scrollHeight)\")\n            page.wait_for_timeout(1000)\n            \n            # 采集新数据\n            new_items = page.locator(item_selector).all_text_contents()\n            items.extend(new_items[last_count:])\n            last_count = len(items)\n            \n            # 检查是否到底\n            if len(items) == last_count:\n                break\n        \n        return items[:max_items]\n```\n\n### 5. 反爬对抗\n\n```python\nclass AntiDetection:\n    \"\"\"反检测\"\"\"\n    \n    @staticmethod\n    def stealth(context: BrowserContext):\n        \"\"\"Stealth模式\"\"\"\n        # 随机User-Agent\n        ua_list = [\n            \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0\",\n            \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Safari/17.2\",\n        ]\n        context.set_extra_http_headers({\"User-Agent\": random.choice(ua_list)})\n        \n        # 注入反检测脚本\n        context.add_init_script(\"\"\"\n            Object.defineProperty(navigator, 'webdriver', { get: () => undefined });\n            window.chrome = { runtime: {} };\n        \"\"\")\n    \n    @staticmethod\n    def human_delay(page: Page, min_ms=50, max_ms=200):\n        \"\"\"模拟人类延迟\"\"\"\n        import random\n        import time\n        time.sleep(random.uniform(min_ms, max_ms) / 1000)\n```\n\n## Puppeteer 实现\n\n```javascript\nconst { chromium } = require('puppeteer');\n\nclass BrowserAutomation {\n  async init() {\n    this.browser = await chromium.launch({ \n      headless: true,\n      args: ['--disable-blink-features=AutomationControlled']\n    });\n    \n    // Stealth\n    const context = await this.browser.newContext({\n      viewport: { width: 1920, height: 1080 },\n      userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'\n    });\n    \n    // Remove webdriver flag\n    await context.addInitScript(() => {\n      Object.defineProperty(navigator, 'webdriver', { get: () => false });\n    });\n    \n    this.page = await context.newPage();\n  }\n  \n  async goto(url) {\n    await this.page.goto(url, { waitUntil: 'networkidle2' });\n    await this.page.waitForTimeout(1000);\n  }\n  \n  async click(selector) {\n    await this.page.locator(selector).first().click();\n  }\n  \n  async type(selector, text) {\n    await this.page.locator(selector).fill(text);\n  }\n  \n  async extract(selector) {\n    return await this.page.locator(selector).allTextContents();\n  }\n  \n  async screenshot(path) {\n    await this.page.screenshot({ path, fullPage: true });\n  }\n  \n  async close() {\n    await this.browser.close();\n  }\n}\n```\n\n## 常见任务模板\n\n### 登录 + 数据采集\n```python\nwith BrowserAgent() as agent:\n    page = agent.new_page()\n    \n    # 登录\n    page.goto(\"https://example.com/login\")\n    page.fill(\"#username\", \"user@example.com\")\n    page.fill(\"#password\", \"password123\")\n    page.click(\"button[type='submit']\")\n    page.wait_for_url(\"**/dashboard\")\n    \n    # 采集数据\n    page.goto(\"https://example.com/data\")\n    data = WebScraper().scrape_table(page, \"table.data\")\n    \n    print(data)\n```\n\n### 批量截图\n```python\nurls = [\"https://site1.com\", \"https://site2.com\", \"https://site3.com\"]\nwith BrowserAgent() as agent:\n    for i, url in enumerate(urls):\n        page = agent.new_page()\n        page.goto(url, wait_until=\"networkidle\")\n        page.screenshot(path=f\"screenshot_{i}.png\")\n        page.close()\n```\n\n## 最佳实践\n\n1. **等待策略**：用 `wait_for_selector` 而不是固定sleep\n2. **重试机制**：网络不稳定时自动重试\n3. **异常处理**：每个操作都要try-catch\n4. **资源清理**：总是关闭page和context\n5. **隐身模式**：每个任务用独立的context避免cookies污染\n\n## Usage\n\n1. Install the skill\n2. Configure as needed\n3. Run with OpenClaw\n","topics":["Playwright","Puppeteer","Browser Automation","Web Scraping"],"tags":{"latest":"1.0.0"},"stats":{"comments":0,"downloads":397,"installsAllTime":15,"installsCurrent":2,"stars":0,"versions":1},"createdAt":1777863083060,"updatedAt":1778492842520},"latestVersion":{"version":"1.0.0","createdAt":1777863083060,"changelog":"Initial release of the browser automation agent.\n\n- Supports AI-driven web automation using Playwright or Puppeteer for scraping, form filling, and UI interaction.\n- Implements a base framework for automated task execution and step planning.\n- Features intelligent element location, smart form filling/extraction, and data scraping tools for tables and cards.\n- Designed for compatibility with multiple web automation libraries and scenarios.\n- Includes sample code and technical documentation.","license":"MIT-0"},"metadata":null,"owner":{"handle":"sky-lv","userId":"s17fgkeb63szvtadtmm753m0gd84e4vz","displayName":"SKY-lv","image":"https://avatars.githubusercontent.com/u/259750852?v=4"},"moderation":{"isSuspicious":false,"isMalwareBlocked":false,"verdict":"clean","reasonCodes":["review.llm_review"],"summary":"Review: review.llm_review","engineVersion":"v2.4.24","updatedAt":1780090739586}}