
一、引言
在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打造一个强大的网页处理能力!