Playwright与Puppeteer实战教程:让AI拥有“看懂“网页的能力

一、引言

在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. 两者的对比

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

三、环境准备与安装

1. Node.js环境要求

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

bash 复制代码
# 检查Node.js版本
node --version

# 检查npm版本
npm --version

2. 安装Playwright

bash 复制代码
# 创建项目目录
mkdir my-playwright-project
cd my-playwright-project

# 初始化项目
npm init -y

# 安装Playwright
npm install playwright

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

3. 安装Puppeteer

bash 复制代码
# 创建项目目录
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,推荐使用官方提供的镜像:

bash 复制代码
# Playwright Docker镜像
docker pull mcr.microsoft.com/playwright:latest

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

四、Playwright实战教程

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

javascript 复制代码
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. 高级操作:等待元素并交互

javascript 复制代码
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. 滚动截长图

javascript 复制代码
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. 网络请求拦截与修改

javascript 复制代码
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代码

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. 基础使用:页面操作

javascript 复制代码
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. 表单填写与提交

javascript 复制代码
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

javascript 复制代码
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. 性能分析

javascript 复制代码
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封装脚本

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配置

yaml 复制代码
---
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. 资源优化

javascript 复制代码
// 使用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. 错误处理

javascript 复制代码
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. 清理资源

javascript 复制代码
// 确保浏览器进程被正确关闭
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打造一个强大的网页处理能力!

相关推荐
TDengine (老段)2 小时前
TDengine IDMP 组态面板 —— 图元
大数据·数据库·人工智能·物联网·时序数据库·tdengine
进击ing小白2 小时前
OpenCv之图像缩放翻转和拼接
人工智能·opencv·计算机视觉
小妖同学学AI2 小时前
AI数字人,赋予教学新的生命力
人工智能
天云数据2 小时前
OpenClaw-RL:边聊边学的统一智能体强化学习框架
人工智能
Mintopia2 小时前
团队 AI 协作开发:一套把产品快速落地的工程化方案
前端·人工智能
深小乐2 小时前
DeepWiki:AI 重塑 GitHub 代码理解新体验
人工智能
我想问问天2 小时前
OpenClaw + Obsidian 实现 AI 记忆持久化:打造真正有长期记忆的 AI 助手
人工智能
茉莉玫瑰花茶2 小时前
COZE - 2
ai·coze