在这里插入图片描述

一、引言

在AI Agent时代,如何让大语言模型“看懂”网页是一个核心挑战。与Jina Reader API这种“外包”方案不同,Playwright和Puppeteer提供了一种“本地化”的解决方案——直接在服务器上运行无头浏览器,真正渲染页面、执行JavaScript、交互操作。

本文将详细介绍这两款工具的使用方法、适用场景以及实战技巧,帮助您在资源允许的情况下,为AI Agent打造一个功能强大的网页理解与自动化处理系统。

二、Playwright与Puppeteer概述

1. 什么是Playwright?

Playwright是由Microsoft开发的一个Node.js库,专门用于自动化浏览器操作和端到端测试。它的核心特点包括:

  • 多浏览器支持:支持Chrome、Firefox、WebKit(Safari)
  • 自动等待机制:内置智能等待,不再需要手动添加sleep
  • 强大的录制功能:可以录制用户操作并自动生成代码
  • 网络拦截:可以Mock网络请求,测试各种场景
  • 移动端模拟:内置移动设备模拟器

2. 什么是Puppeteer?

Puppeteer是由Google Chrome团队开发的Node.js库,专门用于控制Chrome或Chromium。它的特点包括:

  • 轻量级:相比Playwright更加轻量,启动更快
  • Chrome优化:对Chrome的支持最为完善
  • PDF生成:内置高质量的PDF生成功能
  • 性能监控:提供详细的性能分析工具

3. 两者的对比

特性PlaywrightPuppeteer
浏览器支持Chrome、Firefox、SafariChrome、Chromium
API设计现代、Promise-based回调+Promise
自动等待内置智能等待需手动处理
跨平台优秀一般
社区活跃度非常活跃活跃
学习曲线较平缓较陡峭

三、环境准备与安装

1. Node.js环境要求

确保您的服务器上安装了Node.js(建议v16及以上版本):

# 检查Node.js版本
node --version

# 检查npm版本
npm --version

2. 安装Playwright

# 创建项目目录
mkdir my-playwright-project
cd my-playwright-project

# 初始化项目
npm init -y

# 安装Playwright
npm install playwright

# 安装浏览器(这一步会下载浏览器二进制文件)
npx playwright install chromium
# 或者安装所有浏览器
npx playwright install

3. 安装Puppeteer

# 创建项目目录
mkdir my-puppeteer-project
cd my-puppeteer-project

# 初始化项目
npm init -y

# 安装Puppeteer(会自动下载Chrome)
npm install puppeteer

# 或者安装puppeteer-core(不下载浏览器,适合已有Chrome环境)
npm install puppeteer-core

4. Docker环境配置(推荐)

如果您在服务器上使用Docker,推荐使用官方提供的镜像:

# Playwright Docker镜像
docker pull mcr.microsoft.com/playwright:latest

# 运行容器
docker run -it --rm mcr.microsoft.com/playwright:latest /bin/bash

四、Playwright实战教程

1. 基础使用:打开网页并截图

const { chromium } = require('playwright');

(async () => {
  // 启动浏览器
  const browser = await chromium.launch({
    headless: true,  // 无头模式
    args: ['--no-sandbox']  // Docker环境需要
  });

  // 创建新页面
  const page = await browser.newPage();

  // 设置视口大小
  await page.setViewportSize({ width: 1920, height: 1080 });

  // 访问网页
  await page.goto('https://www.example.com');

  // 截图
  await page.screenshot({ path: 'example.png', fullPage: true });

  // 获取页面标题
  const title = await page.title();
  console.log('页面标题:', title);

  // 关闭浏览器
  await browser.close();
})();

2. 高级操作:等待元素并交互

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage();

  // 访问需要等待的页面
  await page.goto('https://example.com/login');

  // 等待元素出现
  await page.waitForSelector('#username', { state: 'visible' });

  // 输入文本
  await page.fill('#username', 'myusername');
  await page.fill('#password', 'mypassword');

  // 点击按钮
  await page.click('#login-button');

  // 等待导航完成
  await page.waitForLoadState('networkidle');

  // 获取登录后的内容
  const content = await page.content();
  console.log('页面内容长度:', content.length);

  await browser.close();
})();

3. 滚动截长图

const { chromium } = require('playwright');

async function captureFullPage(url, outputPath) {
  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage();

  await page.goto(url, { waitUntil: 'networkidle' });

  // 获取页面总高度
  const totalHeight = await page.evaluate(() => {
    return document.documentElement.scrollHeight;
  });

  // 设置视口高度为总高度
  await page.setViewportSize({
    width: 1920,
    height: totalHeight
  });

  // 截图
  await page.screenshot({
    path: outputPath,
    fullPage: true
  });

  await browser.close();
}

captureFullPage('https://www.example.com', 'fullpage.png')
  .then(() => console.log('截图完成'));

4. 网络请求拦截与修改

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage();

  // 拦截网络请求
  await page.route('**/*', (route) => {
    const url = route.request().url();

    // 阻止广告请求
    if (url.includes('ads') || url.includes('analytics')) {
      route.abort();
    } else {
      route.continue();
    }
  });

  // 监听响应
  page.on('response', (response) => {
    if (response.url().includes('api')) {
      console.log('API响应:', response.status());
    }
  });

  await page.goto('https://www.example.com');
  await browser.close();
})();

5. 执行JavaScript代码

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage();

  await page.goto('https://www.example.com');

  // 在页面上下文中执行JavaScript
  const result = await page.evaluate(() => {
    // 获取所有链接
    const links = Array.from(document.querySelectorAll('a'))
      .map(a => ({
        text: a.textContent,
        href: a.href
      }));
    return links.slice(0, 10);  // 只返回前10个
  });

  console.log('页面链接:', result);

  // 获取页面统计数据
  const stats = await page.evaluate(() => {
    return {
      images: document.images.length,
      scripts: document.scripts.length,
      links: document.links.length,
      bodyText: document.body.innerText.length
    };
  });

  console.log('页面统计:', stats);

  await browser.close();
})();

五、Puppeteer实战教程

1. 基础使用:页面操作

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: true,
    args: ['--no-sandbox', '--disable-setuid-sandbox']
  });

  const page = await browser.newPage();

  // 设置User-Agent
  await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');

  // 访问页面
  await page.goto('https://www.example.com', {
    waitUntil: 'networkidle2'
  });

  // 获取标题
  const title = await page.title();
  console.log('标题:', title);

  // 获取特定元素
  const heading = await page.$eval('h1', el => el.textContent);
  console.log('H1内容:', heading);

  await browser.close();
})();

2. 表单填写与提交

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();

  await page.goto('https://example.com/form');

  // 填写表单
  await page.type('#name', '张三');
  await page.type('#email', 'zhangsan@example.com');
  await page.select('#country', 'CN');  // 下拉框

  // 勾选复选框
  await page.click('#agree');

  // 提交表单
  await page.click('button[type="submit"]');

  // 等待导航
  await page.waitForNavigation({ waitUntil: 'networkidle0' });

  // 获取结果
  const result = await page.$eval('.result', el => el.textContent);
  console.log('提交结果:', result);

  await browser.close();
})();

3. 生成PDF

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();

  // 设置内容(也可以用page.goto读取URL)
  await page.setContent(`
    <html>
      <head><style>
        body { font-family: Arial; padding: 40px; }
        h1 { color: #333; }
      </style></head>
      <body>
        <h1>测试PDF文档</h1>
        <p>这是一个使用Puppeteer生成的PDF文件。</p>
        <p>支持中文内容和复杂排版。</p>
      </body>
    </html>
  `);

  // 生成PDF
  await page.pdf({
    path: 'document.pdf',
    format: 'A4',
    printBackground: true,
    margin: {
      top: '20mm',
      bottom: '20mm',
      left: '20mm',
      right: '20mm'
    }
  });

  console.log('PDF生成完成');
  await browser.close();
})();

4. 性能分析

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();

  // 启用性能监控
  await page.setCacheEnabled(false);

  const client = await page.target().createCDPSession();
  await client.send('Performance.enable');

  // 访问页面
  await page.goto('https://www.example.com');

  // 获取性能指标
  const metrics = await client.send('Performance.getMetrics');
  console.log('性能指标:', metrics.metrics);

  // 获取页面加载时间
  const timing = await page.evaluate(() => {
    return {
      domContentLoaded: performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart,
      loadComplete: performance.timing.loadEventEnd - performance.timing.navigationStart,
      firstPaint: performance.getEntriesByType('paint')[0]?.startTime
    };
  });
  console.log('加载时间:', timing);

  await browser.close();
})();

六、与OpenClaw集成方案

1. 创建Python封装脚本

#!/usr/bin/env python3
"""
Playwright/Puppeteer封装脚本,供OpenClaw调用
"""

import asyncio
import json
import sys
import subprocess
from pathlib import Path

async def playwright_screenshot(url: str, output: str = "screenshot.png") -> dict:
    """截取网页截图"""
    script = f"""
    const {{ chromium }} = require('playwright');
    (async () => {{
      const browser = await chromium.launch({{ headless: true }});
      const page = await browser.newPage();
      await page.goto('{url}', {{ waitUntil: 'networkidle' }});
      await page.screenshot({{ path: '{output}', fullPage: true }});
      await browser.close();
    }})();
    """
    result = subprocess.run(
        ["node", "-e", script],
        capture_output=True,
        text=True
    )
    return {
        "success": result.returncode == 0,
        "output": output,
        "error": result.stderr if result.returncode != 0 else None
    }

async def playwright_content(url: str) -> dict:
    """获取网页内容"""
    script = f"""
    const {{ chromium }} = require('playwright');
    (async () => {{
      const browser = await chromium.launch({{ headless: true }});
      const page = await browser.newPage();
      await page.goto('{url}', {{ waitUntil: 'networkidle' }});
      const content = await page.content();
      await browser.close();
      console.log(content);
    }})();
    """
    result = subprocess.run(
        ["node", "-e", script],
        capture_output=True,
        text=True
    )
    return {
        "success": result.returncode == 0,
        "content": result.stdout,
        "error": result.stderr if result.returncode != 0 else None
    }

if __name__ == "__main__":
    command = sys.argv[1] if len(sys.argv) > 1 else "help"
    url = sys.argv[2] if len(sys.argv) > 2 else ""

    if command == "screenshot":
        result = asyncio.run(playwright_screenshot(url))
    elif command == "content":
        result = asyncio.run(playwright_content(url))
    else:
        result = {"error": "Unknown command"}

    print(json.dumps(result, ensure_ascii=False))

2. OpenClaw Skill配置

---
name: browser_automation
description: Use Playwright/Puppeteer for browser automation and webpage analysis
user-invocable: true
---

# Browser Automation Skill

Use this skill when you need to:
- Take screenshots of webpages
- Extract dynamic content from JavaScript-rendered pages
- Interact with web applications (forms, buttons, etc.)
- Monitor webpage changes
- Capture full-page screenshots

## Implementation

Execute the wrapper script with commands:
- `python browser.py screenshot <url>` - Take a screenshot
- `python browser.py content <url>` - Get page content

## Notes
- This skill requires Node.js and Playwright/Puppeteer installed
- For heavy usage, consider using Jina Reader API as an alternative
- Resource-intensive operations may slow down the server

七、最佳实践与注意事项

1. 资源优化

// 使用Chromium的轻量级配置
const browser = await chromium.launch({
  headless: true,
  args: [
    '--no-sandbox',
    '--disable-setuid-sandbox',
    '--disable-dev-shm-usage',  // 减少内存使用
    '--disable-gpu',  // 禁用GPU加速
    '--no-zygote',
    '--single-process'  // 单进程模式
  ]
});

2. 错误处理

async function safeGoto(page, url, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
      return true;
    } catch (error) {
      console.log(`尝试 ${i + 1} 失败:`, error.message);
      if (i < retries - 1) {
        await page.waitForTimeout(1000);  // 等待后重试
      }
    }
  }
  return false;
}

3. 清理资源

// 确保浏览器进程被正确关闭
try {
  // 操作代码
} finally {
  await browser.close();  // 总是关闭浏览器
}

// 或者使用try-with-resources模式
const browser = await chromium.launch();
try {
  // 操作代码
} finally {
  await browser.close();
}

八、适用场景对比

适合使用Playwright/Puppeteer的场景

场景说明
登录认证需要处理Cookie、Session的复杂登录
表单交互需要填写表单、点击按钮等操作
动态内容JavaScript渲染的SPA应用
截图需求需要精确控制截图范围和格式
自动化测试端到端测试、回归测试
性能监控分析页面加载性能和Core Web Vitals

适合使用Jina Reader API的场景

场景说明
资源受限2核2G等轻量服务器
简单内容提取文章、文档、新闻等静态页面
大批量处理需要高并发的网页抓取
成本控制不想维护浏览器基础设施
快速原型快速验证想法

九、总结

Playwright和Puppeteer是两款强大的浏览器自动化工具,它们为AI Agent提供了“真正看懂网页”的能力。与Jina Reader API这种外包方案相比,它们能够:

  • 完整渲染:真正执行JavaScript,获取动态内容
  • 交互能力:填写表单、点击按钮、滚动页面
  • 精细控制:精确控制截图、等待、元素操作
  • 测试能力:内置测试框架,支持自动化测试

但同时,它们也有明显的局限性:

  • 资源消耗:需要较多的CPU和内存
  • 维护成本:浏览器版本需要同步更新
  • 速度较慢:启动和执行都比API方案慢

在实际项目中,建议根据具体需求选择合适的方案:

  • 资源充足、需要复杂交互 → 选择Playwright
  • 轻量服务器、简单需求 → 选择Jina Reader API
  • 最佳实践 → 结合两者,互补使用

希望这篇教程能帮助您更好地理解和使用Playwright/Puppeteer,为您的AI Agent打造一个强大的网页处理能力!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐