前言
在 AI 快速发展的背景下,将大语言模型(LLM)与浏览器自动化工具结合,正成为一种趋势。Puppeteer 作为领先的浏览器自动化框架,与大模型的结合让智能 Agent 能够理解和执行复杂的网页操作任务。本文将深入探讨 Puppeteer 的技术细节,以及大模型如何理解、调用和处理 Puppeteer 参数的核心机制。
第一部分:Puppeteer 框架深度解析
1.1 Puppeteer 核心架构
Puppeteer 是 Google Chrome 团队开发的 Node.js 库,通过 DevTools Protocol(CDP)与 Chrome/Chromium 通信。核心架构包括:
┌─────────────────┐
│ Node.js 应用 │
└────────┬────────┘
│ DevTools Protocol (WebSocket)
↓
┌─────────────────┐
│ Puppeteer API │
│ (高级抽象层) │
└────────┬────────┘
│
↓
┌─────────────────┐
│ Chrome/Chromium │
│ 浏览器实例 │
└─────────────────┘
关键组件:
- Browser:浏览器实例
- BrowserContext:隔离的上下文(类似无痕窗口)
- Page:页面(标签页)
- Frame:页面中的框架
- ElementHandle:DOM 元素句柄
- JSHandle:JavaScript 对象句柄
1.2 核心 API 详解
Browser 实例管理
const puppeteer = require('puppeteer');
// 启动浏览器
const browser = await puppeteer.launch({
headless: true, // 无头模式
executablePath: '/path/to/chrome', // 自定义 Chrome 路径
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-accelerated-2d-canvas',
'--disable-gpu'
],
defaultViewport: {
width: 1920,
height: 1080
},
timeout: 30000
});
// 创建新的上下文(隔离环境)
const context = await browser.createBrowserContext({
viewport: { width: 1280, height: 720 },
userAgent: 'Mozilla/5.0...'
});
// 在新上下文中创建页面
const page = await context.newPage();
Page 导航与等待
// 基础导航
await page.goto('https://example.com', {
waitUntil: 'networkidle2', // 等待网络空闲
timeout: 60000
});
// 等待策略
await page.goto(url, {
waitUntil: [
'load', // 等待 load 事件
'domcontentloaded', // DOM 加载完成
'networkidle0', // 网络完全空闲(0个连接)
'networkidle2' // 网络基本空闲(≤2个连接)
]
});
// 等待元素出现
await page.waitForSelector('.content', {
visible: true, // 元素可见
hidden: false, // 元素不隐藏
timeout: 5000
});
// 等待函数返回 true
await page.waitForFunction(
() => document.readyState === 'complete',
{ timeout: 30000 }
);
// 等待导航完成
await page.waitForNavigation({
waitUntil: 'networkidle2'
});
元素查找与交互
// 查找元素(返回 ElementHandle)
const element = await page.$('.search-input'); // 单个元素
const elements = await page.$$('.item'); // 多个元素
// 在元素上执行操作
await page.click('.button');
await page.type('.input', 'text', { delay: 100 }); // 模拟人类输入
await page.focus('.input');
await page.hover('.menu-item');
// 使用 ElementHandle
const button = await page.$('#submit');
await button.click();
await button.hover();
const text = await button.evaluate(el => el.textContent);
// XPath 选择器
const elements = await page.$x('//button[contains(text(), "提交")]');
JavaScript 执行
// 在页面上下文中执行代码(返回序列化结果)
const result = await page.evaluate(() => {
return document.title;
});
// 传递参数到页面上下文
const data = await page.evaluate((selector, attribute) => {
const element = document.querySelector(selector);
return element ? element.getAttribute(attribute) : null;
}, '.link', 'href');
// 返回非序列化对象(JSHandle)
const handle = await page.evaluateHandle(() => {
return window.myObject;
});
// 在元素上下文中执行
const element = await page.$('.container');
const text = await element.evaluate(el => el.innerText);
// 执行并等待 Promise
await page.evaluate(async () => {
await new Promise(resolve => setTimeout(resolve, 1000));
// 执行异步操作
});
网络拦截与监控
// 拦截请求
await page.setRequestInterception(true);
page.on('request', request => {
// 修改请求
if (request.url().includes('api.example.com')) {
request.continue({
headers: {
...request.headers(),
'X-Custom-Header': 'value'
}
});
} else {
request.continue();
}
});
// 拦截响应
page.on('response', response => {
if (response.url().includes('api.example.com')) {
response.json().then(data => {
console.log('API Response:', data);
});
}
});
// 阻止资源加载(提升性能)
await page.setRequestInterception(true);
page.on('request', request => {
const resourceType = request.resourceType();
if (['image', 'stylesheet', 'font'].includes(resourceType)) {
request.abort();
} else {
request.continue();
}
});
截图与 PDF
// 全页面截图
await page.screenshot({
path: 'screenshot.png',
fullPage: true,
type: 'png',
quality: 90 // 仅对 jpeg 有效
});
// 元素截图
const element = await page.$('.widget');
await element.screenshot({ path: 'widget.png' });
// PDF 生成
await page.pdf({
path: 'document.pdf',
format: 'A4',
printBackground: true,
margin: {
top: '20px',
right: '20px',
bottom: '20px',
left: '20px'
}
});
1.3 高级特性
并行页面管理
// 创建多个页面并行处理
const pages = await Promise.all([
browser.newPage(),
browser.newPage(),
browser.newPage()
]);
const results = await Promise.all(
pages.map(async (page, index) => {
await page.goto(urls[index]);
return await page.title();
})
);
// 清理
await Promise.all(pages.map(page => page.close()));
性能监控
// 启用性能跟踪
await page.tracing.start({
path: 'trace.json',
screenshots: true
});
// 执行操作
await page.goto('https://example.com');
// 停止跟踪
await page.tracing.stop();
// 获取性能指标
const metrics = await page.metrics();
console.log(metrics);
// {
// Timestamp: 1234567890,
// Documents: 10,
// Frames: 5,
// JSEventListeners: 100,
// Nodes: 500,
// LayoutCount: 20,
// RecalcStyleCount: 15,
// ...
// }
Cookie 和 LocalStorage 管理
// 设置 Cookie
await page.setCookie({
name: 'session',
value: 'abc123',
domain: 'example.com',
path: '/',
expires: Date.now() / 1000 + 3600 // 1小时后过期
});
// 获取 Cookie
const cookies = await page.cookies();
console.log(cookies);
// 清除 Cookie
await page.deleteCookie(...cookies);
// 操作 LocalStorage
await page.evaluate(() => {
localStorage.setItem('key', 'value');
const value = localStorage.getItem('key');
});
第二部分:大模型与 Puppeteer 的联合应用
2.1 应用场景架构
大模型与 Puppeteer 的联合应用通常采用以下架构:
┌─────────────────────────────────────────┐
│ 用户自然语言指令 │
│ "帮我在京东搜索iPhone 15并查看价格" │
└──────────────┬──────────────────────────┘
│
↓
┌─────────────────────────────────────────┐
│ 大语言模型 (LLM) │
│ - 理解用户意图 │
│ - 生成操作步骤 │
│ - 提取关键参数 │
└──────────────┬──────────────────────────┘
│
↓ JSON Schema / Function Calling
┌─────────────────────────────────────────┐
│ Puppeteer 操作序列生成器 │
│ - 将步骤转换为 Puppeteer API 调用 │
│ - 参数验证与转换 │
└──────────────┬──────────────────────────┘
│
↓
┌─────────────────────────────────────────┐
│ Puppeteer 执行引擎 │
│ - 执行浏览器操作 │
│ - 捕获执行结果 │
└──────────────┬──────────────────────────┘
│
↓
┌─────────────────────────────────────────┐
│ 结果处理与反馈 │
│ - 提取页面内容 │
│ - 大模型分析结果 │
│ - 生成自然语言回复 │
└─────────────────────────────────────────┘
2.2 Function Calling 模式
大模型通过 Function Calling 调用 Puppeteer。定义一组工具函数,大模型决定何时调用:
定义工具函数 Schema
const puppeteerTools = [
{
type: "function",
function: {
name: "navigate_to_page",
description: "导航到指定网页",
parameters: {
type: "object",
properties: {
url: {
type: "string",
description: "目标网页的完整URL"
},
wait_until: {
type: "string",
enum: ["load", "domcontentloaded", "networkidle0", "networkidle2"],
description: "等待策略,networkidle2表示等待网络基本空闲"
}
},
required: ["url"]
}
}
},
{
type: "function",
function: {
name: "click_element",
description: "点击页面上的元素",
parameters: {
type: "object",
properties: {
selector: {
type: "string",
description: "CSS选择器或XPath,用于定位元素"
},
selector_type: {
type: "string",
enum: ["css", "xpath"],
description: "选择器类型,css或xpath"
},
wait_for_visible: {
type: "boolean",
description: "是否等待元素可见后再点击"
}
},
required: ["selector", "selector_type"]
}
}
},
{
type: "function",
function: {
name: "input_text",
description: "在输入框中输入文本",
parameters: {
type: "object",
properties: {
selector: {
type: "string",
description: "输入框的CSS选择器"
},
text: {
type: "string",
description: "要输入的文本内容"
},
clear_first: {
type: "boolean",
description: "输入前是否清空原有内容"
},
delay: {
type: "number",
description: "每个字符之间的延迟毫秒数,模拟人类输入"
}
},
required: ["selector", "text"]
}
}
},
{
type: "function",
function: {
name: "extract_content",
description: "从页面提取内容",
parameters: {
type: "object",
properties: {
selector: {
type: "string",
description: "要提取内容的元素选择器"
},
extraction_type: {
type: "string",
enum: ["text", "html", "attribute", "all_links", "table_data"],
description: "提取类型:text-文本内容,html-HTML代码,attribute-属性值,all_links-所有链接,table_data-表格数据"
},
attribute_name: {
type: "string",
description: "当extraction_type为attribute时,指定属性名(如href、src等)"
}
},
required: ["selector", "extraction_type"]
}
}
},
{
type: "function",
function: {
name: "wait_for_element",
description: "等待页面元素出现或消失",
parameters: {
type: "object",
properties: {
selector: {
type: "string",
description: "要等待的元素选择器"
},
state: {
type: "string",
enum: ["visible", "hidden", "attached"],
description: "等待状态:visible-元素可见,hidden-元素隐藏,attached-元素添加到DOM"
},
timeout: {
type: "number",
description: "超时时间(毫秒),默认5000"
}
},
required: ["selector", "state"]
}
}
},
{
type: "function",
function: {
name: "execute_javascript",
description: "在页面上下文中执行JavaScript代码",
parameters: {
type: "object",
properties: {
code: {
type: "string",
description: "要执行的JavaScript代码"
},
return_result: {
type: "boolean",
description: "是否返回执行结果"
}
},
required: ["code"]
}
}
},
{
type: "function",
function: {
name: "take_screenshot",
description: "截取页面或元素的截图",
parameters: {
type: "object",
properties: {
selector: {
type: "string",
description: "要截图的元素选择器,为空则截取整个页面"
},
full_page: {
type: "boolean",
description: "是否截取整个页面(仅当selector为空时有效)"
}
},
required: []
}
}
}
];
实现工具函数
class PuppeteerAgent {
constructor() {
this.browser = null;
this.page = null;
}
async init() {
this.browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
this.page = await this.browser.newPage();
}
async navigate_to_page(url, wait_until = 'networkidle2') {
try {
await this.page.goto(url, { waitUntil: wait_until, timeout: 60000 });
return {
success: true,
message: `成功导航到 ${url}`,
title: await this.page.title()
};
} catch (error) {
return {
success: false,
message: `导航失败: ${error.message}`
};
}
}
async click_element(selector, selector_type = 'css', wait_for_visible = true) {
try {
if (selector_type === 'xpath') {
const elements = await this.page.$x(selector);
if (elements.length === 0) {
return { success: false, message: `未找到元素: ${selector}` };
}
if (wait_for_visible) {
await this.page.waitForXPath(selector, { visible: true });
}
await elements[0].click();
} else {
if (wait_for_visible) {
await this.page.waitForSelector(selector, { visible: true });
}
await this.page.click(selector);
}
return {
success: true,
message: `成功点击元素: ${selector}`
};
} catch (error) {
return {
success: false,
message: `点击失败: ${error.message}`
};
}
}
async input_text(selector, text, clear_first = true, delay = 50) {
try {
await this.page.waitForSelector(selector, { visible: true });
if (clear_first) {
await this.page.click(selector, { clickCount: 3 }); // 三击选中全部
await this.page.keyboard.press('Backspace');
}
await this.page.type(selector, text, { delay });
return {
success: true,
message: `成功输入文本到 ${selector}`
};
} catch (error) {
return {
success: false,
message: `输入失败: ${error.message}`
};
}
}
async extract_content(selector, extraction_type, attribute_name = null) {
try {
let result;
switch (extraction_type) {
case 'text':
result = await this.page.$eval(selector, el => el.textContent.trim());
break;
case 'html':
result = await this.page.$eval(selector, el => el.innerHTML);
break;
case 'attribute':
if (!attribute_name) {
return { success: false, message: 'attribute类型需要指定attribute_name' };
}
result = await this.page.$eval(selector, (el, attr) => el.getAttribute(attr), attribute_name);
break;
case 'all_links':
result = await this.page.$$eval('a', links =>
links.map(link => ({
text: link.textContent.trim(),
href: link.href
}))
);
break;
case 'table_data':
result = await this.page.$eval(selector, table => {
const rows = Array.from(table.querySelectorAll('tr'));
return rows.map(row => {
const cells = Array.from(row.querySelectorAll('td, th'));
return cells.map(cell => cell.textContent.trim());
});
});
break;
default:
return { success: false, message: `不支持的提取类型: ${extraction_type}` };
}
return {
success: true,
data: result,
message: `成功提取内容`
};
} catch (error) {
return {
success: false,
message: `提取失败: ${error.message}`
};
}
}
async wait_for_element(selector, state, timeout = 5000) {
try {
const options = { timeout };
if (state === 'visible') {
await this.page.waitForSelector(selector, { visible: true, ...options });
} else if (state === 'hidden') {
await this.page.waitForSelector(selector, { hidden: true, ...options });
} else if (state === 'attached') {
await this.page.waitForSelector(selector, options);
}
return {
success: true,
message: `元素 ${selector} 已${state === 'visible' ? '可见' : state === 'hidden' ? '隐藏' : '附加'}`
};
} catch (error) {
return {
success: false,
message: `等待超时或失败: ${error.message}`
};
}
}
async execute_javascript(code, return_result = true) {
try {
const result = return_result
? await this.page.evaluate(code)
: await this.page.evaluate(() => { eval(code); });
return {
success: true,
data: return_result ? result : null,
message: 'JavaScript执行成功'
};
} catch (error) {
return {
success: false,
message: `执行失败: ${error.message}`
};
}
}
async take_screenshot(selector = null, full_page = false) {
try {
const timestamp = Date.now();
const filename = `screenshot_${timestamp}.png`;
if (selector) {
const element = await this.page.$(selector);
if (!element) {
return { success: false, message: `未找到元素: ${selector}` };
}
await element.screenshot({ path: filename });
} else {
await this.page.screenshot({
path: filename,
fullPage: full_page
});
}
return {
success: true,
filename: filename,
message: '截图成功'
};
} catch (error) {
return {
success: false,
message: `截图失败: ${error.message}`
};
}
}
async close() {
if (this.browser) {
await this.browser.close();
}
}
}
2.3 大模型调用流程
完整的 Agent 实现
const { OpenAI } = require('openai');
class IntelligentWebAgent {
constructor(apiKey) {
this.openai = new OpenAI({ apiKey });
this.agent = new PuppeteerAgent();
this.conversationHistory = [];
}
async init() {
await this.agent.init();
}
async executeTask(userQuery) {
// 构建消息历史
const messages = [
{
role: "system",
content: `你是一个智能网页操作助手。你可以使用提供的工具函数来控制浏览器执行各种操作。
请遵循以下原则:
1. 仔细分析用户的需求,将其分解为多个步骤
2. 每次调用工具函数后,检查返回结果
3. 如果操作失败,尝试替代方案
4. 在提取内容时,使用合适的选择器
5. 对于动态加载的内容,使用wait_for_element等待元素出现
6. 完成任务后,总结执行结果并回复用户`
},
...this.conversationHistory,
{
role: "user",
content: userQuery
}
];
// 调用大模型,使用 function calling
const response = await this.openai.chat.completions.create({
model: "gpt-4",
messages: messages,
tools: puppeteerTools,
tool_choice: "auto", // 让模型决定是否调用工具
temperature: 0.7
});
const message = response.choices[0].message;
// 检查是否需要调用工具
if (message.tool_calls && message.tool_calls.length > 0) {
const toolResults = [];
for (const toolCall of message.tool_calls) {
const functionName = toolCall.function.name;
const functionArgs = JSON.parse(toolCall.function.arguments);
console.log(`🔧 调用工具: ${functionName}`, functionArgs);
// 执行 Puppeteer 操作
const result = await this.agent[functionName](...Object.values(functionArgs));
toolResults.push({
tool_call_id: toolCall.id,
role: "tool",
name: functionName,
content: JSON.stringify(result)
});
}
// 将工具结果发送回大模型
this.conversationHistory.push(message);
this.conversationHistory.push(...toolResults);
// 继续对话,让大模型处理结果
return await this.executeTask(""); // 递归调用,处理工具结果
} else {
// 大模型返回最终回复
this.conversationHistory.push(message);
return message.content;
}
}
async close() {
await this.agent.close();
}
}
// 使用示例
(async () => {
const agent = new IntelligentWebAgent(process.env.OPENAI_API_KEY);
await agent.init();
const result = await agent.executeTask("帮我在百度搜索'Puppeteer教程',然后告诉我第一页有多少个搜索结果");
console.log(result);
await agent.close();
})();
2.4 大模型理解参数的技术细节
参数理解机制
大模型理解 Puppeteer 参数主要依赖:
- Schema 描述质量
- 清晰的参数说明
- 枚举值定义
- 类型约束
- 示例值
- 上下文学习
- 通过示例学习
- 错误反馈调整
- 多轮对话优化
- 参数验证与转换
class ParameterValidator {
// 验证和标准化选择器
static normalizeSelector(selector, selector_type) {
if (selector_type === 'css') {
// 验证 CSS 选择器语法
if (!this.isValidCSSSelector(selector)) {
throw new Error(`无效的CSS选择器: ${selector}`);
}
return selector;
} else if (selector_type === 'xpath') {
// XPath 选择器
if (!selector.startsWith('//') && !selector.startsWith('.//')) {
// 大模型可能生成不完整的XPath,尝试修复
if (selector.includes('text()')) {
return `//*[contains(text(), "${selector.match(/text\(\)='(.+?)'/)?.[1] || ''}")]`;
}
}
return selector;
}
return selector;
}
// 智能选择器生成(当大模型提供的选择器不准确时)
static async generateAlternativeSelector(page, description) {
// 使用大模型生成更准确的选择器
const prompt = `根据以下描述,生成一个准确的CSS选择器:
描述:${description}
页面HTML结构:
${await page.content()}`;
// 调用大模型生成选择器
// ... 实现细节
}
// 参数类型转换
static convertParameter(value, expectedType) {
switch (expectedType) {
case 'number':
const num = parseFloat(value);
if (isNaN(num)) throw new Error(`无法转换为数字: ${value}`);
return num;
case 'boolean':
if (typeof value === 'string') {
return value.toLowerCase() === 'true';
}
return Boolean(value);
case 'array':
return Array.isArray(value) ? value : [value];
default:
return value;
}
}
}
错误处理与重试机制
class RobustPuppeteerAgent extends PuppeteerAgent {
async executeWithRetry(operation, maxRetries = 3) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
return await operation();
} catch (error) {
lastError = error;
console.log(`尝试 ${i + 1}/${maxRetries} 失败: ${error.message}`);
// 分析错误类型,决定是否重试
if (this.isRetryableError(error)) {
await this.delay(1000 * (i + 1)); // 指数退避
continue;
} else {
throw error; // 不可重试的错误直接抛出
}
}
}
throw lastError;
}
isRetryableError(error) {
const retryablePatterns = [
/timeout/i,
/Navigation timeout/i,
/Target closed/i,
/Protocol error/i
];
return retryablePatterns.some(pattern => pattern.test(error.message));
}
// 智能等待元素(处理动态加载)
async smartWaitForElement(selector, timeout = 10000) {
try {
// 首先尝试标准等待
await this.page.waitForSelector(selector, { timeout });
return true;
} catch (error) {
// 如果失败,尝试滚动页面(触发懒加载)
await this.page.evaluate(() => {
window.scrollTo(0, document.body.scrollHeight);
});
// 再次尝试等待
try {
await this.page.waitForSelector(selector, { timeout: 5000 });
return true;
} catch (e) {
// 最后尝试:使用更宽松的选择器
const alternativeSelectors = this.generateAlternativeSelectors(selector);
for (const altSelector of alternativeSelectors) {
try {
await this.page.waitForSelector(altSelector, { timeout: 2000 });
return true;
} catch (e2) {
continue;
}
}
return false;
}
}
}
}
2.5 高级应用:视觉理解结合
结合视觉模型(如 GPT-4 Vision)和 Puppeteer,可以实现更智能的交互:
class VisionEnhancedAgent extends PuppeteerAgent {
async findElementByDescription(description) {
// 截取页面截图
const screenshot = await this.page.screenshot({ encoding: 'base64' });
// 使用视觉模型识别元素位置
const visionResponse = await this.openai.chat.completions.create({
model: "gpt-4-vision-preview",
messages: [
{
role: "user",
content: [
{
type: "text",
text: `在这个网页截图中,找到以下描述的元素,并返回它的CSS选择器或坐标位置:${description}`
},
{
type: "image_url",
image_url: {
url: `data:image/png;base64,${screenshot}`
}
}
]
}
]
});
// 解析视觉模型的回复,提取选择器
const selector = this.extractSelectorFromResponse(visionResponse.choices[0].message.content);
return selector;
}
async clickByDescription(description) {
const selector = await this.findElementByDescription(description);
return await this.click_element(selector, 'css', true);
}
}
第三部分:实际应用案例
案例1:电商价格监控 Agent
class PriceMonitorAgent extends IntelligentWebAgent {
async monitorPrice(productUrl, targetPrice) {
const task = `
1. 访问商品页面:${productUrl}
2. 等待页面加载完成
3. 提取商品价格
4. 提取商品名称
5. 比较价格是否低于目标价格 ${targetPrice}
6. 如果低于目标价格,发送通知
`;
return await this.executeTask(task);
}
async extractProductInfo() {
// 使用大模型智能提取结构化数据
const pageContent = await this.agent.page.content();
const extractionPrompt = `
从以下HTML中提取商品信息(JSON格式):
- 商品名称
- 当前价格
- 原价(如果有)
- 库存状态
- 商品评分
HTML内容:
${pageContent.substring(0, 5000)}
`;
const response = await this.openai.chat.completions.create({
model: "gpt-4",
messages: [
{
role: "system",
content: "你是一个数据提取专家,从网页HTML中提取结构化数据,返回JSON格式。"
},
{
role: "user",
content: extractionPrompt
}
],
response_format: { type: "json_object" }
});
return JSON.parse(response.choices[0].message.content);
}
}
案例2:自动化测试生成
class TestGeneratorAgent extends IntelligentWebAgent {
async generateTestScenario(featureDescription) {
const prompt = `
基于以下功能描述,生成详细的测试场景和对应的Puppeteer操作步骤:
功能描述:${featureDescription}
请生成:
1. 测试用例列表
2. 每个测试用例的详细操作步骤
3. 预期的验证点
`;
const testPlan = await this.openai.chat.completions.create({
model: "gpt-4",
messages: [{ role: "user", content: prompt }],
tools: puppeteerTools
});
// 执行测试用例
return await this.executeTestPlan(testPlan);
}
}
案例3:内容抓取与摘要
class ContentScraperAgent extends IntelligentWebAgent {
async scrapeAndSummarize(url, summaryLength = 200) {
await this.agent.navigate_to_page(url);
// 提取主要内容
const mainContent = await this.agent.page.evaluate(() => {
// 移除脚本、样式等
const scripts = document.querySelectorAll('script, style, nav, footer, header');
scripts.forEach(el => el.remove());
// 提取正文内容
const article = document.querySelector('article, main, .content, .post');
return article ? article.innerText : document.body.innerText;
});
// 使用大模型生成摘要
const summary = await this.openai.chat.completions.create({
model: "gpt-4",
messages: [
{
role: "system",
content: "你是一个内容摘要专家,生成准确、简洁的文章摘要。"
},
{
role: "user",
content: `请为以下内容生成一个${summaryLength}字左右的摘要:\n\n${mainContent.substring(0, 8000)}`
}
]
});
return {
url: url,
content: mainContent,
summary: summary.choices[0].message.content
};
}
}
第四部分:最佳实践与优化
4.1 性能优化
// 1. 浏览器实例复用
class BrowserPool {
constructor(poolSize = 3) {
this.pool = [];
this.poolSize = poolSize;
}
async getBrowser() {
if (this.pool.length > 0) {
return this.pool.pop();
}
return await puppeteer.launch({ headless: true });
}
async releaseBrowser(browser) {
if (this.pool.length < this.poolSize) {
this.pool.push(browser);
} else {
await browser.close();
}
}
}
// 2. 请求拦截优化(禁用不必要的资源)
async function optimizePage(page) {
await page.setRequestInterception(true);
page.on('request', (req) => {
const resourceType = req.resourceType();
if (['image', 'stylesheet', 'font', 'media'].includes(resourceType)) {
req.abort();
} else {
req.continue();
}
});
}
// 3. 并行处理
async function parallelScraping(urls) {
const browser = await puppeteer.launch();
const results = await Promise.all(
urls.map(async (url) => {
const page = await browser.newPage();
await page.goto(url);
const title = await page.title();
await page.close();
return { url, title };
})
);
await browser.close();
return results;
}
4.2 错误处理策略
class ErrorHandler {
static async handlePuppeteerError(error, context) {
const errorInfo = {
message: error.message,
stack: error.stack,
context: context,
timestamp: new Date().toISOString()
};
// 根据错误类型采取不同策略
if (error.message.includes('Navigation timeout')) {
// 导航超时:尝试刷新或使用更宽松的等待策略
return {
action: 'retry',
strategy: 'use_networkidle0',
errorInfo
};
} else if (error.message.includes('Target closed')) {
// 目标关闭:重新创建页面
return {
action: 'recreate_page',
errorInfo
};
} else if (error.message.includes('No node found')) {
// 元素未找到:尝试替代选择器
return {
action: 'try_alternative_selector',
errorInfo
};
}
return {
action: 'log_and_continue',
errorInfo
};
}
}
4.3 安全考虑
// 1. 沙箱隔离
const browser = await puppeteer.launch({
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-accelerated-2d-canvas',
'--disable-gpu',
'--disable-features=IsolateOrigins,site-per-process'
]
});
// 2. 内容安全策略
await page.setContent(html, {
waitUntil: 'networkidle0'
});
// 3. 限制执行时间
async function executeWithTimeout(fn, timeout = 30000) {
return Promise.race([
fn(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('操作超时')), timeout)
)
]);
}
总结
Puppeteer 与大模型的结合为浏览器自动化带来新的可能性。通过 Function Calling,大模型能够理解和执行复杂的网页操作,而 Puppeteer 提供稳定的执行环境。
关键技术点:
- 清晰的工具函数 Schema 定义
- 参数验证与智能转换
- 错误处理与重试机制
- 视觉理解增强交互能力
- 性能优化与资源管理
未来方向:
- 多模态理解(文本+视觉)
- 更强的上下文记忆
- 自主错误恢复
- 更自然的对话交互
这种结合将推动智能 Agent 在自动化领域的应用。