{"id":2576,"date":"2026-01-04T10:05:02","date_gmt":"2026-01-04T02:05:02","guid":{"rendered":"https:\/\/sanlangcode.com\/?p=2576"},"modified":"2026-01-04T10:05:02","modified_gmt":"2026-01-04T02:05:02","slug":"puppeteer-%e4%b8%8e%e5%a4%a7%e6%a8%a1%e5%9e%8b%e6%b7%b1%e5%ba%a6%e8%9e%8d%e5%90%88%ef%bc%9a%e6%b5%8f%e8%a7%88%e5%99%a8%e8%87%aa%e5%8a%a8%e5%8c%96%e7%9a%84%e6%99%ba%e8%83%bd%e8%bf%9b%e5%8c%96","status":"publish","type":"post","link":"https:\/\/sanlangcode.com\/index.php\/2026\/01\/04\/puppeteer-%e4%b8%8e%e5%a4%a7%e6%a8%a1%e5%9e%8b%e6%b7%b1%e5%ba%a6%e8%9e%8d%e5%90%88%ef%bc%9a%e6%b5%8f%e8%a7%88%e5%99%a8%e8%87%aa%e5%8a%a8%e5%8c%96%e7%9a%84%e6%99%ba%e8%83%bd%e8%bf%9b%e5%8c%96\/","title":{"rendered":"Puppeteer \u4e0e\u5927\u6a21\u578b\u6df1\u5ea6\u878d\u5408\uff1a\u6d4f\u89c8\u5668\u81ea\u52a8\u5316\u7684\u667a\u80fd\u8fdb\u5316"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">\u524d\u8a00<\/h2>\n\n\n\n<p>\u5728 AI \u5feb\u901f\u53d1\u5c55\u7684\u80cc\u666f\u4e0b\uff0c\u5c06\u5927\u8bed\u8a00\u6a21\u578b\uff08LLM\uff09\u4e0e\u6d4f\u89c8\u5668\u81ea\u52a8\u5316\u5de5\u5177\u7ed3\u5408\uff0c\u6b63\u6210\u4e3a\u4e00\u79cd\u8d8b\u52bf\u3002Puppeteer \u4f5c\u4e3a\u9886\u5148\u7684\u6d4f\u89c8\u5668\u81ea\u52a8\u5316\u6846\u67b6\uff0c\u4e0e\u5927\u6a21\u578b\u7684\u7ed3\u5408\u8ba9\u667a\u80fd Agent \u80fd\u591f\u7406\u89e3\u548c\u6267\u884c\u590d\u6742\u7684\u7f51\u9875\u64cd\u4f5c\u4efb\u52a1\u3002\u672c\u6587\u5c06\u6df1\u5165\u63a2\u8ba8 Puppeteer \u7684\u6280\u672f\u7ec6\u8282\uff0c\u4ee5\u53ca\u5927\u6a21\u578b\u5982\u4f55\u7406\u89e3\u3001\u8c03\u7528\u548c\u5904\u7406 Puppeteer \u53c2\u6570\u7684\u6838\u5fc3\u673a\u5236\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u7b2c\u4e00\u90e8\u5206\uff1aPuppeteer \u6846\u67b6\u6df1\u5ea6\u89e3\u6790<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1.1 Puppeteer \u6838\u5fc3\u67b6\u6784<\/h3>\n\n\n\n<p>Puppeteer \u662f Google Chrome \u56e2\u961f\u5f00\u53d1\u7684 Node.js \u5e93\uff0c\u901a\u8fc7 DevTools Protocol\uff08CDP\uff09\u4e0e Chrome\/Chromium \u901a\u4fe1\u3002\u6838\u5fc3\u67b6\u6784\u5305\u62ec\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502   Node.js \u5e94\u7528  \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n         \u2502 DevTools Protocol (WebSocket)\n         \u2193\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502  Puppeteer API  \u2502\n\u2502  (\u9ad8\u7ea7\u62bd\u8c61\u5c42)    \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n         \u2502\n         \u2193\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Chrome\/Chromium \u2502\n\u2502  \u6d4f\u89c8\u5668\u5b9e\u4f8b      \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/code><\/pre>\n\n\n\n<p>\u5173\u952e\u7ec4\u4ef6\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Browser\uff1a\u6d4f\u89c8\u5668\u5b9e\u4f8b<\/li>\n\n\n\n<li>BrowserContext\uff1a\u9694\u79bb\u7684\u4e0a\u4e0b\u6587\uff08\u7c7b\u4f3c\u65e0\u75d5\u7a97\u53e3\uff09<\/li>\n\n\n\n<li>Page\uff1a\u9875\u9762\uff08\u6807\u7b7e\u9875\uff09<\/li>\n\n\n\n<li>Frame\uff1a\u9875\u9762\u4e2d\u7684\u6846\u67b6<\/li>\n\n\n\n<li>ElementHandle\uff1aDOM \u5143\u7d20\u53e5\u67c4<\/li>\n\n\n\n<li>JSHandle\uff1aJavaScript \u5bf9\u8c61\u53e5\u67c4<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">1.2 \u6838\u5fc3 API \u8be6\u89e3<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">Browser \u5b9e\u4f8b\u7ba1\u7406<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>const puppeteer = require('puppeteer');\n\n\/\/ \u542f\u52a8\u6d4f\u89c8\u5668\nconst browser = await puppeteer.launch({\n  headless: true,           \/\/ \u65e0\u5934\u6a21\u5f0f\n  executablePath: '\/path\/to\/chrome', \/\/ \u81ea\u5b9a\u4e49 Chrome \u8def\u5f84\n  args: &#91;\n    '--no-sandbox',\n    '--disable-setuid-sandbox',\n    '--disable-dev-shm-usage',\n    '--disable-accelerated-2d-canvas',\n    '--disable-gpu'\n  ],\n  defaultViewport: {\n    width: 1920,\n    height: 1080\n  },\n  timeout: 30000\n});\n\n\/\/ \u521b\u5efa\u65b0\u7684\u4e0a\u4e0b\u6587\uff08\u9694\u79bb\u73af\u5883\uff09\nconst context = await browser.createBrowserContext({\n  viewport: { width: 1280, height: 720 },\n  userAgent: 'Mozilla\/5.0...'\n});\n\n\/\/ \u5728\u65b0\u4e0a\u4e0b\u6587\u4e2d\u521b\u5efa\u9875\u9762\nconst page = await context.newPage();<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Page \u5bfc\u822a\u4e0e\u7b49\u5f85<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u57fa\u7840\u5bfc\u822a\nawait page.goto('https:\/\/example.com', {\n  waitUntil: 'networkidle2',  \/\/ \u7b49\u5f85\u7f51\u7edc\u7a7a\u95f2\n  timeout: 60000\n});\n\n\/\/ \u7b49\u5f85\u7b56\u7565\nawait page.goto(url, {\n  waitUntil: &#91;\n    'load',              \/\/ \u7b49\u5f85 load \u4e8b\u4ef6\n    'domcontentloaded',  \/\/ DOM \u52a0\u8f7d\u5b8c\u6210\n    'networkidle0',      \/\/ \u7f51\u7edc\u5b8c\u5168\u7a7a\u95f2\uff080\u4e2a\u8fde\u63a5\uff09\n    'networkidle2'       \/\/ \u7f51\u7edc\u57fa\u672c\u7a7a\u95f2\uff08\u22642\u4e2a\u8fde\u63a5\uff09\n  ]\n});\n\n\/\/ \u7b49\u5f85\u5143\u7d20\u51fa\u73b0\nawait page.waitForSelector('.content', {\n  visible: true,         \/\/ \u5143\u7d20\u53ef\u89c1\n  hidden: false,         \/\/ \u5143\u7d20\u4e0d\u9690\u85cf\n  timeout: 5000\n});\n\n\/\/ \u7b49\u5f85\u51fd\u6570\u8fd4\u56de true\nawait page.waitForFunction(\n  () =&gt; document.readyState === 'complete',\n  { timeout: 30000 }\n);\n\n\/\/ \u7b49\u5f85\u5bfc\u822a\u5b8c\u6210\nawait page.waitForNavigation({\n  waitUntil: 'networkidle2'\n});<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">\u5143\u7d20\u67e5\u627e\u4e0e\u4ea4\u4e92<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u67e5\u627e\u5143\u7d20\uff08\u8fd4\u56de ElementHandle\uff09\nconst element = await page.$('.search-input');  \/\/ \u5355\u4e2a\u5143\u7d20\nconst elements = await page.$$('.item');        \/\/ \u591a\u4e2a\u5143\u7d20\n\n\/\/ \u5728\u5143\u7d20\u4e0a\u6267\u884c\u64cd\u4f5c\nawait page.click('.button');\nawait page.type('.input', 'text', { delay: 100 });  \/\/ \u6a21\u62df\u4eba\u7c7b\u8f93\u5165\nawait page.focus('.input');\nawait page.hover('.menu-item');\n\n\/\/ \u4f7f\u7528 ElementHandle\nconst button = await page.$('#submit');\nawait button.click();\nawait button.hover();\nconst text = await button.evaluate(el =&gt; el.textContent);\n\n\/\/ XPath \u9009\u62e9\u5668\nconst elements = await page.$x('\/\/button&#91;contains(text(), \"\u63d0\u4ea4\")]');<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">JavaScript \u6267\u884c<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u5728\u9875\u9762\u4e0a\u4e0b\u6587\u4e2d\u6267\u884c\u4ee3\u7801\uff08\u8fd4\u56de\u5e8f\u5217\u5316\u7ed3\u679c\uff09\nconst result = await page.evaluate(() =&gt; {\n  return document.title;\n});\n\n\/\/ \u4f20\u9012\u53c2\u6570\u5230\u9875\u9762\u4e0a\u4e0b\u6587\nconst data = await page.evaluate((selector, attribute) =&gt; {\n  const element = document.querySelector(selector);\n  return element ? element.getAttribute(attribute) : null;\n}, '.link', 'href');\n\n\/\/ \u8fd4\u56de\u975e\u5e8f\u5217\u5316\u5bf9\u8c61\uff08JSHandle\uff09\nconst handle = await page.evaluateHandle(() =&gt; {\n  return window.myObject;\n});\n\n\/\/ \u5728\u5143\u7d20\u4e0a\u4e0b\u6587\u4e2d\u6267\u884c\nconst element = await page.$('.container');\nconst text = await element.evaluate(el =&gt; el.innerText);\n\n\/\/ \u6267\u884c\u5e76\u7b49\u5f85 Promise\nawait page.evaluate(async () =&gt; {\n  await new Promise(resolve =&gt; setTimeout(resolve, 1000));\n  \/\/ \u6267\u884c\u5f02\u6b65\u64cd\u4f5c\n});<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">\u7f51\u7edc\u62e6\u622a\u4e0e\u76d1\u63a7<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u62e6\u622a\u8bf7\u6c42\nawait page.setRequestInterception(true);\npage.on('request', request =&gt; {\n  \/\/ \u4fee\u6539\u8bf7\u6c42\n  if (request.url().includes('api.example.com')) {\n    request.continue({\n      headers: {\n        ...request.headers(),\n        'X-Custom-Header': 'value'\n      }\n    });\n  } else {\n    request.continue();\n  }\n});\n\n\/\/ \u62e6\u622a\u54cd\u5e94\npage.on('response', response =&gt; {\n  if (response.url().includes('api.example.com')) {\n    response.json().then(data =&gt; {\n      console.log('API Response:', data);\n    });\n  }\n});\n\n\/\/ \u963b\u6b62\u8d44\u6e90\u52a0\u8f7d\uff08\u63d0\u5347\u6027\u80fd\uff09\nawait page.setRequestInterception(true);\npage.on('request', request =&gt; {\n  const resourceType = request.resourceType();\n  if (&#91;'image', 'stylesheet', 'font'].includes(resourceType)) {\n    request.abort();\n  } else {\n    request.continue();\n  }\n});<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">\u622a\u56fe\u4e0e PDF<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u5168\u9875\u9762\u622a\u56fe\nawait page.screenshot({\n  path: 'screenshot.png',\n  fullPage: true,\n  type: 'png',\n  quality: 90  \/\/ \u4ec5\u5bf9 jpeg \u6709\u6548\n});\n\n\/\/ \u5143\u7d20\u622a\u56fe\nconst element = await page.$('.widget');\nawait element.screenshot({ path: 'widget.png' });\n\n\/\/ PDF \u751f\u6210\nawait page.pdf({\n  path: 'document.pdf',\n  format: 'A4',\n  printBackground: true,\n  margin: {\n    top: '20px',\n    right: '20px',\n    bottom: '20px',\n    left: '20px'\n  }\n});<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">1.3 \u9ad8\u7ea7\u7279\u6027<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">\u5e76\u884c\u9875\u9762\u7ba1\u7406<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u521b\u5efa\u591a\u4e2a\u9875\u9762\u5e76\u884c\u5904\u7406\nconst pages = await Promise.all(&#91;\n  browser.newPage(),\n  browser.newPage(),\n  browser.newPage()\n]);\n\nconst results = await Promise.all(\n  pages.map(async (page, index) =&gt; {\n    await page.goto(urls&#91;index]);\n    return await page.title();\n  })\n);\n\n\/\/ \u6e05\u7406\nawait Promise.all(pages.map(page =&gt; page.close()));<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">\u6027\u80fd\u76d1\u63a7<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u542f\u7528\u6027\u80fd\u8ddf\u8e2a\nawait page.tracing.start({ \n  path: 'trace.json',\n  screenshots: true \n});\n\n\/\/ \u6267\u884c\u64cd\u4f5c\nawait page.goto('https:\/\/example.com');\n\n\/\/ \u505c\u6b62\u8ddf\u8e2a\nawait page.tracing.stop();\n\n\/\/ \u83b7\u53d6\u6027\u80fd\u6307\u6807\nconst metrics = await page.metrics();\nconsole.log(metrics);\n\/\/ {\n\/\/   Timestamp: 1234567890,\n\/\/   Documents: 10,\n\/\/   Frames: 5,\n\/\/   JSEventListeners: 100,\n\/\/   Nodes: 500,\n\/\/   LayoutCount: 20,\n\/\/   RecalcStyleCount: 15,\n\/\/   ...\n\/\/ }<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Cookie \u548c LocalStorage \u7ba1\u7406<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u8bbe\u7f6e Cookie\nawait page.setCookie({\n  name: 'session',\n  value: 'abc123',\n  domain: 'example.com',\n  path: '\/',\n  expires: Date.now() \/ 1000 + 3600  \/\/ 1\u5c0f\u65f6\u540e\u8fc7\u671f\n});\n\n\/\/ \u83b7\u53d6 Cookie\nconst cookies = await page.cookies();\nconsole.log(cookies);\n\n\/\/ \u6e05\u9664 Cookie\nawait page.deleteCookie(...cookies);\n\n\/\/ \u64cd\u4f5c LocalStorage\nawait page.evaluate(() =&gt; {\n  localStorage.setItem('key', 'value');\n  const value = localStorage.getItem('key');\n});<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u7b2c\u4e8c\u90e8\u5206\uff1a\u5927\u6a21\u578b\u4e0e Puppeteer \u7684\u8054\u5408\u5e94\u7528<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">2.1 \u5e94\u7528\u573a\u666f\u67b6\u6784<\/h3>\n\n\n\n<p>\u5927\u6a21\u578b\u4e0e Puppeteer \u7684\u8054\u5408\u5e94\u7528\u901a\u5e38\u91c7\u7528\u4ee5\u4e0b\u67b6\u6784\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502         \u7528\u6237\u81ea\u7136\u8bed\u8a00\u6307\u4ee4                  \u2502\n\u2502    \"\u5e2e\u6211\u5728\u4eac\u4e1c\u641c\u7d22iPhone 15\u5e76\u67e5\u770b\u4ef7\u683c\"    \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n               \u2502\n               \u2193\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502        \u5927\u8bed\u8a00\u6a21\u578b (LLM)                  \u2502\n\u2502  - \u7406\u89e3\u7528\u6237\u610f\u56fe                          \u2502\n\u2502  - \u751f\u6210\u64cd\u4f5c\u6b65\u9aa4                          \u2502\n\u2502  - \u63d0\u53d6\u5173\u952e\u53c2\u6570                          \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n               \u2502\n               \u2193 JSON Schema \/ Function Calling\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502      Puppeteer \u64cd\u4f5c\u5e8f\u5217\u751f\u6210\u5668             \u2502\n\u2502  - \u5c06\u6b65\u9aa4\u8f6c\u6362\u4e3a Puppeteer API \u8c03\u7528        \u2502\n\u2502  - \u53c2\u6570\u9a8c\u8bc1\u4e0e\u8f6c\u6362                        \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n               \u2502\n               \u2193\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502        Puppeteer \u6267\u884c\u5f15\u64ce                \u2502\n\u2502  - \u6267\u884c\u6d4f\u89c8\u5668\u64cd\u4f5c                        \u2502\n\u2502  - \u6355\u83b7\u6267\u884c\u7ed3\u679c                          \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n               \u2502\n               \u2193\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502         \u7ed3\u679c\u5904\u7406\u4e0e\u53cd\u9988                   \u2502\n\u2502  - \u63d0\u53d6\u9875\u9762\u5185\u5bb9                          \u2502\n\u2502  - \u5927\u6a21\u578b\u5206\u6790\u7ed3\u679c                        \u2502\n\u2502  - \u751f\u6210\u81ea\u7136\u8bed\u8a00\u56de\u590d                      \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2.2 Function Calling \u6a21\u5f0f<\/h3>\n\n\n\n<p>\u5927\u6a21\u578b\u901a\u8fc7 Function Calling \u8c03\u7528 Puppeteer\u3002\u5b9a\u4e49\u4e00\u7ec4\u5de5\u5177\u51fd\u6570\uff0c\u5927\u6a21\u578b\u51b3\u5b9a\u4f55\u65f6\u8c03\u7528\uff1a<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">\u5b9a\u4e49\u5de5\u5177\u51fd\u6570 Schema<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>const puppeteerTools = &#91;\n  {\n    type: \"function\",\n    function: {\n      name: \"navigate_to_page\",\n      description: \"\u5bfc\u822a\u5230\u6307\u5b9a\u7f51\u9875\",\n      parameters: {\n        type: \"object\",\n        properties: {\n          url: {\n            type: \"string\",\n            description: \"\u76ee\u6807\u7f51\u9875\u7684\u5b8c\u6574URL\"\n          },\n          wait_until: {\n            type: \"string\",\n            enum: &#91;\"load\", \"domcontentloaded\", \"networkidle0\", \"networkidle2\"],\n            description: \"\u7b49\u5f85\u7b56\u7565\uff0cnetworkidle2\u8868\u793a\u7b49\u5f85\u7f51\u7edc\u57fa\u672c\u7a7a\u95f2\"\n          }\n        },\n        required: &#91;\"url\"]\n      }\n    }\n  },\n  {\n    type: \"function\",\n    function: {\n      name: \"click_element\",\n      description: \"\u70b9\u51fb\u9875\u9762\u4e0a\u7684\u5143\u7d20\",\n      parameters: {\n        type: \"object\",\n        properties: {\n          selector: {\n            type: \"string\",\n            description: \"CSS\u9009\u62e9\u5668\u6216XPath\uff0c\u7528\u4e8e\u5b9a\u4f4d\u5143\u7d20\"\n          },\n          selector_type: {\n            type: \"string\",\n            enum: &#91;\"css\", \"xpath\"],\n            description: \"\u9009\u62e9\u5668\u7c7b\u578b\uff0ccss\u6216xpath\"\n          },\n          wait_for_visible: {\n            type: \"boolean\",\n            description: \"\u662f\u5426\u7b49\u5f85\u5143\u7d20\u53ef\u89c1\u540e\u518d\u70b9\u51fb\"\n          }\n        },\n        required: &#91;\"selector\", \"selector_type\"]\n      }\n    }\n  },\n  {\n    type: \"function\",\n    function: {\n      name: \"input_text\",\n      description: \"\u5728\u8f93\u5165\u6846\u4e2d\u8f93\u5165\u6587\u672c\",\n      parameters: {\n        type: \"object\",\n        properties: {\n          selector: {\n            type: \"string\",\n            description: \"\u8f93\u5165\u6846\u7684CSS\u9009\u62e9\u5668\"\n          },\n          text: {\n            type: \"string\",\n            description: \"\u8981\u8f93\u5165\u7684\u6587\u672c\u5185\u5bb9\"\n          },\n          clear_first: {\n            type: \"boolean\",\n            description: \"\u8f93\u5165\u524d\u662f\u5426\u6e05\u7a7a\u539f\u6709\u5185\u5bb9\"\n          },\n          delay: {\n            type: \"number\",\n            description: \"\u6bcf\u4e2a\u5b57\u7b26\u4e4b\u95f4\u7684\u5ef6\u8fdf\u6beb\u79d2\u6570\uff0c\u6a21\u62df\u4eba\u7c7b\u8f93\u5165\"\n          }\n        },\n        required: &#91;\"selector\", \"text\"]\n      }\n    }\n  },\n  {\n    type: \"function\",\n    function: {\n      name: \"extract_content\",\n      description: \"\u4ece\u9875\u9762\u63d0\u53d6\u5185\u5bb9\",\n      parameters: {\n        type: \"object\",\n        properties: {\n          selector: {\n            type: \"string\",\n            description: \"\u8981\u63d0\u53d6\u5185\u5bb9\u7684\u5143\u7d20\u9009\u62e9\u5668\"\n          },\n          extraction_type: {\n            type: \"string\",\n            enum: &#91;\"text\", \"html\", \"attribute\", \"all_links\", \"table_data\"],\n            description: \"\u63d0\u53d6\u7c7b\u578b\uff1atext-\u6587\u672c\u5185\u5bb9\uff0chtml-HTML\u4ee3\u7801\uff0cattribute-\u5c5e\u6027\u503c\uff0call_links-\u6240\u6709\u94fe\u63a5\uff0ctable_data-\u8868\u683c\u6570\u636e\"\n          },\n          attribute_name: {\n            type: \"string\",\n            description: \"\u5f53extraction_type\u4e3aattribute\u65f6\uff0c\u6307\u5b9a\u5c5e\u6027\u540d\uff08\u5982href\u3001src\u7b49\uff09\"\n          }\n        },\n        required: &#91;\"selector\", \"extraction_type\"]\n      }\n    }\n  },\n  {\n    type: \"function\",\n    function: {\n      name: \"wait_for_element\",\n      description: \"\u7b49\u5f85\u9875\u9762\u5143\u7d20\u51fa\u73b0\u6216\u6d88\u5931\",\n      parameters: {\n        type: \"object\",\n        properties: {\n          selector: {\n            type: \"string\",\n            description: \"\u8981\u7b49\u5f85\u7684\u5143\u7d20\u9009\u62e9\u5668\"\n          },\n          state: {\n            type: \"string\",\n            enum: &#91;\"visible\", \"hidden\", \"attached\"],\n            description: \"\u7b49\u5f85\u72b6\u6001\uff1avisible-\u5143\u7d20\u53ef\u89c1\uff0chidden-\u5143\u7d20\u9690\u85cf\uff0cattached-\u5143\u7d20\u6dfb\u52a0\u5230DOM\"\n          },\n          timeout: {\n            type: \"number\",\n            description: \"\u8d85\u65f6\u65f6\u95f4\uff08\u6beb\u79d2\uff09\uff0c\u9ed8\u8ba45000\"\n          }\n        },\n        required: &#91;\"selector\", \"state\"]\n      }\n    }\n  },\n  {\n    type: \"function\",\n    function: {\n      name: \"execute_javascript\",\n      description: \"\u5728\u9875\u9762\u4e0a\u4e0b\u6587\u4e2d\u6267\u884cJavaScript\u4ee3\u7801\",\n      parameters: {\n        type: \"object\",\n        properties: {\n          code: {\n            type: \"string\",\n            description: \"\u8981\u6267\u884c\u7684JavaScript\u4ee3\u7801\"\n          },\n          return_result: {\n            type: \"boolean\",\n            description: \"\u662f\u5426\u8fd4\u56de\u6267\u884c\u7ed3\u679c\"\n          }\n        },\n        required: &#91;\"code\"]\n      }\n    }\n  },\n  {\n    type: \"function\",\n    function: {\n      name: \"take_screenshot\",\n      description: \"\u622a\u53d6\u9875\u9762\u6216\u5143\u7d20\u7684\u622a\u56fe\",\n      parameters: {\n        type: \"object\",\n        properties: {\n          selector: {\n            type: \"string\",\n            description: \"\u8981\u622a\u56fe\u7684\u5143\u7d20\u9009\u62e9\u5668\uff0c\u4e3a\u7a7a\u5219\u622a\u53d6\u6574\u4e2a\u9875\u9762\"\n          },\n          full_page: {\n            type: \"boolean\",\n            description: \"\u662f\u5426\u622a\u53d6\u6574\u4e2a\u9875\u9762\uff08\u4ec5\u5f53selector\u4e3a\u7a7a\u65f6\u6709\u6548\uff09\"\n          }\n        },\n        required: &#91;]\n      }\n    }\n  }\n];<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">\u5b9e\u73b0\u5de5\u5177\u51fd\u6570<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>class PuppeteerAgent {\n  constructor() {\n    this.browser = null;\n    this.page = null;\n  }\n\n  async init() {\n    this.browser = await puppeteer.launch({\n      headless: true,\n      args: &#91;'--no-sandbox', '--disable-setuid-sandbox']\n    });\n    this.page = await this.browser.newPage();\n  }\n\n  async navigate_to_page(url, wait_until = 'networkidle2') {\n    try {\n      await this.page.goto(url, { waitUntil: wait_until, timeout: 60000 });\n      return {\n        success: true,\n        message: `\u6210\u529f\u5bfc\u822a\u5230 ${url}`,\n        title: await this.page.title()\n      };\n    } catch (error) {\n      return {\n        success: false,\n        message: `\u5bfc\u822a\u5931\u8d25: ${error.message}`\n      };\n    }\n  }\n\n  async click_element(selector, selector_type = 'css', wait_for_visible = true) {\n    try {\n      if (selector_type === 'xpath') {\n        const elements = await this.page.$x(selector);\n        if (elements.length === 0) {\n          return { success: false, message: `\u672a\u627e\u5230\u5143\u7d20: ${selector}` };\n        }\n        if (wait_for_visible) {\n          await this.page.waitForXPath(selector, { visible: true });\n        }\n        await elements&#91;0].click();\n      } else {\n        if (wait_for_visible) {\n          await this.page.waitForSelector(selector, { visible: true });\n        }\n        await this.page.click(selector);\n      }\n      return {\n        success: true,\n        message: `\u6210\u529f\u70b9\u51fb\u5143\u7d20: ${selector}`\n      };\n    } catch (error) {\n      return {\n        success: false,\n        message: `\u70b9\u51fb\u5931\u8d25: ${error.message}`\n      };\n    }\n  }\n\n  async input_text(selector, text, clear_first = true, delay = 50) {\n    try {\n      await this.page.waitForSelector(selector, { visible: true });\n      if (clear_first) {\n        await this.page.click(selector, { clickCount: 3 }); \/\/ \u4e09\u51fb\u9009\u4e2d\u5168\u90e8\n        await this.page.keyboard.press('Backspace');\n      }\n      await this.page.type(selector, text, { delay });\n      return {\n        success: true,\n        message: `\u6210\u529f\u8f93\u5165\u6587\u672c\u5230 ${selector}`\n      };\n    } catch (error) {\n      return {\n        success: false,\n        message: `\u8f93\u5165\u5931\u8d25: ${error.message}`\n      };\n    }\n  }\n\n  async extract_content(selector, extraction_type, attribute_name = null) {\n    try {\n      let result;\n\n      switch (extraction_type) {\n        case 'text':\n          result = await this.page.$eval(selector, el =&gt; el.textContent.trim());\n          break;\n        case 'html':\n          result = await this.page.$eval(selector, el =&gt; el.innerHTML);\n          break;\n        case 'attribute':\n          if (!attribute_name) {\n            return { success: false, message: 'attribute\u7c7b\u578b\u9700\u8981\u6307\u5b9aattribute_name' };\n          }\n          result = await this.page.$eval(selector, (el, attr) =&gt; el.getAttribute(attr), attribute_name);\n          break;\n        case 'all_links':\n          result = await this.page.$$eval('a', links =&gt; \n            links.map(link =&gt; ({\n              text: link.textContent.trim(),\n              href: link.href\n            }))\n          );\n          break;\n        case 'table_data':\n          result = await this.page.$eval(selector, table =&gt; {\n            const rows = Array.from(table.querySelectorAll('tr'));\n            return rows.map(row =&gt; {\n              const cells = Array.from(row.querySelectorAll('td, th'));\n              return cells.map(cell =&gt; cell.textContent.trim());\n            });\n          });\n          break;\n        default:\n          return { success: false, message: `\u4e0d\u652f\u6301\u7684\u63d0\u53d6\u7c7b\u578b: ${extraction_type}` };\n      }\n\n      return {\n        success: true,\n        data: result,\n        message: `\u6210\u529f\u63d0\u53d6\u5185\u5bb9`\n      };\n    } catch (error) {\n      return {\n        success: false,\n        message: `\u63d0\u53d6\u5931\u8d25: ${error.message}`\n      };\n    }\n  }\n\n  async wait_for_element(selector, state, timeout = 5000) {\n    try {\n      const options = { timeout };\n\n      if (state === 'visible') {\n        await this.page.waitForSelector(selector, { visible: true, ...options });\n      } else if (state === 'hidden') {\n        await this.page.waitForSelector(selector, { hidden: true, ...options });\n      } else if (state === 'attached') {\n        await this.page.waitForSelector(selector, options);\n      }\n\n      return {\n        success: true,\n        message: `\u5143\u7d20 ${selector} \u5df2${state === 'visible' ? '\u53ef\u89c1' : state === 'hidden' ? '\u9690\u85cf' : '\u9644\u52a0'}`\n      };\n    } catch (error) {\n      return {\n        success: false,\n        message: `\u7b49\u5f85\u8d85\u65f6\u6216\u5931\u8d25: ${error.message}`\n      };\n    }\n  }\n\n  async execute_javascript(code, return_result = true) {\n    try {\n      const result = return_result \n        ? await this.page.evaluate(code)\n        : await this.page.evaluate(() =&gt; { eval(code); });\n\n      return {\n        success: true,\n        data: return_result ? result : null,\n        message: 'JavaScript\u6267\u884c\u6210\u529f'\n      };\n    } catch (error) {\n      return {\n        success: false,\n        message: `\u6267\u884c\u5931\u8d25: ${error.message}`\n      };\n    }\n  }\n\n  async take_screenshot(selector = null, full_page = false) {\n    try {\n      const timestamp = Date.now();\n      const filename = `screenshot_${timestamp}.png`;\n\n      if (selector) {\n        const element = await this.page.$(selector);\n        if (!element) {\n          return { success: false, message: `\u672a\u627e\u5230\u5143\u7d20: ${selector}` };\n        }\n        await element.screenshot({ path: filename });\n      } else {\n        await this.page.screenshot({ \n          path: filename,\n          fullPage: full_page \n        });\n      }\n\n      return {\n        success: true,\n        filename: filename,\n        message: '\u622a\u56fe\u6210\u529f'\n      };\n    } catch (error) {\n      return {\n        success: false,\n        message: `\u622a\u56fe\u5931\u8d25: ${error.message}`\n      };\n    }\n  }\n\n  async close() {\n    if (this.browser) {\n      await this.browser.close();\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2.3 \u5927\u6a21\u578b\u8c03\u7528\u6d41\u7a0b<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">\u5b8c\u6574\u7684 Agent \u5b9e\u73b0<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>const { OpenAI } = require('openai');\n\nclass IntelligentWebAgent {\n  constructor(apiKey) {\n    this.openai = new OpenAI({ apiKey });\n    this.agent = new PuppeteerAgent();\n    this.conversationHistory = &#91;];\n  }\n\n  async init() {\n    await this.agent.init();\n  }\n\n  async executeTask(userQuery) {\n    \/\/ \u6784\u5efa\u6d88\u606f\u5386\u53f2\n    const messages = &#91;\n      {\n        role: \"system\",\n        content: `\u4f60\u662f\u4e00\u4e2a\u667a\u80fd\u7f51\u9875\u64cd\u4f5c\u52a9\u624b\u3002\u4f60\u53ef\u4ee5\u4f7f\u7528\u63d0\u4f9b\u7684\u5de5\u5177\u51fd\u6570\u6765\u63a7\u5236\u6d4f\u89c8\u5668\u6267\u884c\u5404\u79cd\u64cd\u4f5c\u3002\n\n\u8bf7\u9075\u5faa\u4ee5\u4e0b\u539f\u5219\uff1a\n1. \u4ed4\u7ec6\u5206\u6790\u7528\u6237\u7684\u9700\u6c42\uff0c\u5c06\u5176\u5206\u89e3\u4e3a\u591a\u4e2a\u6b65\u9aa4\n2. \u6bcf\u6b21\u8c03\u7528\u5de5\u5177\u51fd\u6570\u540e\uff0c\u68c0\u67e5\u8fd4\u56de\u7ed3\u679c\n3. \u5982\u679c\u64cd\u4f5c\u5931\u8d25\uff0c\u5c1d\u8bd5\u66ff\u4ee3\u65b9\u6848\n4. \u5728\u63d0\u53d6\u5185\u5bb9\u65f6\uff0c\u4f7f\u7528\u5408\u9002\u7684\u9009\u62e9\u5668\n5. \u5bf9\u4e8e\u52a8\u6001\u52a0\u8f7d\u7684\u5185\u5bb9\uff0c\u4f7f\u7528wait_for_element\u7b49\u5f85\u5143\u7d20\u51fa\u73b0\n6. \u5b8c\u6210\u4efb\u52a1\u540e\uff0c\u603b\u7ed3\u6267\u884c\u7ed3\u679c\u5e76\u56de\u590d\u7528\u6237`\n      },\n      ...this.conversationHistory,\n      {\n        role: \"user\",\n        content: userQuery\n      }\n    ];\n\n    \/\/ \u8c03\u7528\u5927\u6a21\u578b\uff0c\u4f7f\u7528 function calling\n    const response = await this.openai.chat.completions.create({\n      model: \"gpt-4\",\n      messages: messages,\n      tools: puppeteerTools,\n      tool_choice: \"auto\",  \/\/ \u8ba9\u6a21\u578b\u51b3\u5b9a\u662f\u5426\u8c03\u7528\u5de5\u5177\n      temperature: 0.7\n    });\n\n    const message = response.choices&#91;0].message;\n\n    \/\/ \u68c0\u67e5\u662f\u5426\u9700\u8981\u8c03\u7528\u5de5\u5177\n    if (message.tool_calls &amp;&amp; message.tool_calls.length &gt; 0) {\n      const toolResults = &#91;];\n\n      for (const toolCall of message.tool_calls) {\n        const functionName = toolCall.function.name;\n        const functionArgs = JSON.parse(toolCall.function.arguments);\n\n        console.log(`\ud83d\udd27 \u8c03\u7528\u5de5\u5177: ${functionName}`, functionArgs);\n\n        \/\/ \u6267\u884c Puppeteer \u64cd\u4f5c\n        const result = await this.agent&#91;functionName](...Object.values(functionArgs));\n\n        toolResults.push({\n          tool_call_id: toolCall.id,\n          role: \"tool\",\n          name: functionName,\n          content: JSON.stringify(result)\n        });\n      }\n\n      \/\/ \u5c06\u5de5\u5177\u7ed3\u679c\u53d1\u9001\u56de\u5927\u6a21\u578b\n      this.conversationHistory.push(message);\n      this.conversationHistory.push(...toolResults);\n\n      \/\/ \u7ee7\u7eed\u5bf9\u8bdd\uff0c\u8ba9\u5927\u6a21\u578b\u5904\u7406\u7ed3\u679c\n      return await this.executeTask(\"\");  \/\/ \u9012\u5f52\u8c03\u7528\uff0c\u5904\u7406\u5de5\u5177\u7ed3\u679c\n    } else {\n      \/\/ \u5927\u6a21\u578b\u8fd4\u56de\u6700\u7ec8\u56de\u590d\n      this.conversationHistory.push(message);\n      return message.content;\n    }\n  }\n\n  async close() {\n    await this.agent.close();\n  }\n}\n\n\/\/ \u4f7f\u7528\u793a\u4f8b\n(async () =&gt; {\n  const agent = new IntelligentWebAgent(process.env.OPENAI_API_KEY);\n  await agent.init();\n\n  const result = await agent.executeTask(\"\u5e2e\u6211\u5728\u767e\u5ea6\u641c\u7d22'Puppeteer\u6559\u7a0b'\uff0c\u7136\u540e\u544a\u8bc9\u6211\u7b2c\u4e00\u9875\u6709\u591a\u5c11\u4e2a\u641c\u7d22\u7ed3\u679c\");\n  console.log(result);\n\n  await agent.close();\n})();<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2.4 \u5927\u6a21\u578b\u7406\u89e3\u53c2\u6570\u7684\u6280\u672f\u7ec6\u8282<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">\u53c2\u6570\u7406\u89e3\u673a\u5236<\/h4>\n\n\n\n<p>\u5927\u6a21\u578b\u7406\u89e3 Puppeteer \u53c2\u6570\u4e3b\u8981\u4f9d\u8d56\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Schema \u63cf\u8ff0\u8d28\u91cf<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u6e05\u6670\u7684\u53c2\u6570\u8bf4\u660e<\/li>\n\n\n\n<li>\u679a\u4e3e\u503c\u5b9a\u4e49<\/li>\n\n\n\n<li>\u7c7b\u578b\u7ea6\u675f<\/li>\n\n\n\n<li>\u793a\u4f8b\u503c<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u4e0a\u4e0b\u6587\u5b66\u4e60<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u901a\u8fc7\u793a\u4f8b\u5b66\u4e60<\/li>\n\n\n\n<li>\u9519\u8bef\u53cd\u9988\u8c03\u6574<\/li>\n\n\n\n<li>\u591a\u8f6e\u5bf9\u8bdd\u4f18\u5316<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u53c2\u6570\u9a8c\u8bc1\u4e0e\u8f6c\u6362<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>class ParameterValidator {\n  \/\/ \u9a8c\u8bc1\u548c\u6807\u51c6\u5316\u9009\u62e9\u5668\n  static normalizeSelector(selector, selector_type) {\n    if (selector_type === 'css') {\n      \/\/ \u9a8c\u8bc1 CSS \u9009\u62e9\u5668\u8bed\u6cd5\n      if (!this.isValidCSSSelector(selector)) {\n        throw new Error(`\u65e0\u6548\u7684CSS\u9009\u62e9\u5668: ${selector}`);\n      }\n      return selector;\n    } else if (selector_type === 'xpath') {\n      \/\/ XPath \u9009\u62e9\u5668\n      if (!selector.startsWith('\/\/') &amp;&amp; !selector.startsWith('.\/\/')) {\n        \/\/ \u5927\u6a21\u578b\u53ef\u80fd\u751f\u6210\u4e0d\u5b8c\u6574\u7684XPath\uff0c\u5c1d\u8bd5\u4fee\u590d\n        if (selector.includes('text()')) {\n          return `\/\/*&#91;contains(text(), \"${selector.match(\/text\\(\\)='(.+?)'\/)?.&#91;1] || ''}\")]`;\n        }\n      }\n      return selector;\n    }\n    return selector;\n  }\n\n  \/\/ \u667a\u80fd\u9009\u62e9\u5668\u751f\u6210\uff08\u5f53\u5927\u6a21\u578b\u63d0\u4f9b\u7684\u9009\u62e9\u5668\u4e0d\u51c6\u786e\u65f6\uff09\n  static async generateAlternativeSelector(page, description) {\n    \/\/ \u4f7f\u7528\u5927\u6a21\u578b\u751f\u6210\u66f4\u51c6\u786e\u7684\u9009\u62e9\u5668\n    const prompt = `\u6839\u636e\u4ee5\u4e0b\u63cf\u8ff0\uff0c\u751f\u6210\u4e00\u4e2a\u51c6\u786e\u7684CSS\u9009\u62e9\u5668\uff1a\n\u63cf\u8ff0\uff1a${description}\n\u9875\u9762HTML\u7ed3\u6784\uff1a\n${await page.content()}`;\n\n    \/\/ \u8c03\u7528\u5927\u6a21\u578b\u751f\u6210\u9009\u62e9\u5668\n    \/\/ ... \u5b9e\u73b0\u7ec6\u8282\n  }\n\n  \/\/ \u53c2\u6570\u7c7b\u578b\u8f6c\u6362\n  static convertParameter(value, expectedType) {\n    switch (expectedType) {\n      case 'number':\n        const num = parseFloat(value);\n        if (isNaN(num)) throw new Error(`\u65e0\u6cd5\u8f6c\u6362\u4e3a\u6570\u5b57: ${value}`);\n        return num;\n      case 'boolean':\n        if (typeof value === 'string') {\n          return value.toLowerCase() === 'true';\n        }\n        return Boolean(value);\n      case 'array':\n        return Array.isArray(value) ? value : &#91;value];\n      default:\n        return value;\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">\u9519\u8bef\u5904\u7406\u4e0e\u91cd\u8bd5\u673a\u5236<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>class RobustPuppeteerAgent extends PuppeteerAgent {\n  async executeWithRetry(operation, maxRetries = 3) {\n    let lastError;\n\n    for (let i = 0; i &lt; maxRetries; i++) {\n      try {\n        return await operation();\n      } catch (error) {\n        lastError = error;\n        console.log(`\u5c1d\u8bd5 ${i + 1}\/${maxRetries} \u5931\u8d25: ${error.message}`);\n\n        \/\/ \u5206\u6790\u9519\u8bef\u7c7b\u578b\uff0c\u51b3\u5b9a\u662f\u5426\u91cd\u8bd5\n        if (this.isRetryableError(error)) {\n          await this.delay(1000 * (i + 1)); \/\/ \u6307\u6570\u9000\u907f\n          continue;\n        } else {\n          throw error; \/\/ \u4e0d\u53ef\u91cd\u8bd5\u7684\u9519\u8bef\u76f4\u63a5\u629b\u51fa\n        }\n      }\n    }\n\n    throw lastError;\n  }\n\n  isRetryableError(error) {\n    const retryablePatterns = &#91;\n      \/timeout\/i,\n      \/Navigation timeout\/i,\n      \/Target closed\/i,\n      \/Protocol error\/i\n    ];\n\n    return retryablePatterns.some(pattern =&gt; pattern.test(error.message));\n  }\n\n  \/\/ \u667a\u80fd\u7b49\u5f85\u5143\u7d20\uff08\u5904\u7406\u52a8\u6001\u52a0\u8f7d\uff09\n  async smartWaitForElement(selector, timeout = 10000) {\n    try {\n      \/\/ \u9996\u5148\u5c1d\u8bd5\u6807\u51c6\u7b49\u5f85\n      await this.page.waitForSelector(selector, { timeout });\n      return true;\n    } catch (error) {\n      \/\/ \u5982\u679c\u5931\u8d25\uff0c\u5c1d\u8bd5\u6eda\u52a8\u9875\u9762\uff08\u89e6\u53d1\u61d2\u52a0\u8f7d\uff09\n      await this.page.evaluate(() =&gt; {\n        window.scrollTo(0, document.body.scrollHeight);\n      });\n\n      \/\/ \u518d\u6b21\u5c1d\u8bd5\u7b49\u5f85\n      try {\n        await this.page.waitForSelector(selector, { timeout: 5000 });\n        return true;\n      } catch (e) {\n        \/\/ \u6700\u540e\u5c1d\u8bd5\uff1a\u4f7f\u7528\u66f4\u5bbd\u677e\u7684\u9009\u62e9\u5668\n        const alternativeSelectors = this.generateAlternativeSelectors(selector);\n        for (const altSelector of alternativeSelectors) {\n          try {\n            await this.page.waitForSelector(altSelector, { timeout: 2000 });\n            return true;\n          } catch (e2) {\n            continue;\n          }\n        }\n        return false;\n      }\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2.5 \u9ad8\u7ea7\u5e94\u7528\uff1a\u89c6\u89c9\u7406\u89e3\u7ed3\u5408<\/h3>\n\n\n\n<p>\u7ed3\u5408\u89c6\u89c9\u6a21\u578b\uff08\u5982 GPT-4 Vision\uff09\u548c Puppeteer\uff0c\u53ef\u4ee5\u5b9e\u73b0\u66f4\u667a\u80fd\u7684\u4ea4\u4e92\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class VisionEnhancedAgent extends PuppeteerAgent {\n  async findElementByDescription(description) {\n    \/\/ \u622a\u53d6\u9875\u9762\u622a\u56fe\n    const screenshot = await this.page.screenshot({ encoding: 'base64' });\n\n    \/\/ \u4f7f\u7528\u89c6\u89c9\u6a21\u578b\u8bc6\u522b\u5143\u7d20\u4f4d\u7f6e\n    const visionResponse = await this.openai.chat.completions.create({\n      model: \"gpt-4-vision-preview\",\n      messages: &#91;\n        {\n          role: \"user\",\n          content: &#91;\n            {\n              type: \"text\",\n              text: `\u5728\u8fd9\u4e2a\u7f51\u9875\u622a\u56fe\u4e2d\uff0c\u627e\u5230\u4ee5\u4e0b\u63cf\u8ff0\u7684\u5143\u7d20\uff0c\u5e76\u8fd4\u56de\u5b83\u7684CSS\u9009\u62e9\u5668\u6216\u5750\u6807\u4f4d\u7f6e\uff1a${description}`\n            },\n            {\n              type: \"image_url\",\n              image_url: {\n                url: `data:image\/png;base64,${screenshot}`\n              }\n            }\n          ]\n        }\n      ]\n    });\n\n    \/\/ \u89e3\u6790\u89c6\u89c9\u6a21\u578b\u7684\u56de\u590d\uff0c\u63d0\u53d6\u9009\u62e9\u5668\n    const selector = this.extractSelectorFromResponse(visionResponse.choices&#91;0].message.content);\n\n    return selector;\n  }\n\n  async clickByDescription(description) {\n    const selector = await this.findElementByDescription(description);\n    return await this.click_element(selector, 'css', true);\n  }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u7b2c\u4e09\u90e8\u5206\uff1a\u5b9e\u9645\u5e94\u7528\u6848\u4f8b<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\u6848\u4f8b1\uff1a\u7535\u5546\u4ef7\u683c\u76d1\u63a7 Agent<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>class PriceMonitorAgent extends IntelligentWebAgent {\n  async monitorPrice(productUrl, targetPrice) {\n    const task = `\n    1. \u8bbf\u95ee\u5546\u54c1\u9875\u9762\uff1a${productUrl}\n    2. \u7b49\u5f85\u9875\u9762\u52a0\u8f7d\u5b8c\u6210\n    3. \u63d0\u53d6\u5546\u54c1\u4ef7\u683c\n    4. \u63d0\u53d6\u5546\u54c1\u540d\u79f0\n    5. \u6bd4\u8f83\u4ef7\u683c\u662f\u5426\u4f4e\u4e8e\u76ee\u6807\u4ef7\u683c ${targetPrice}\n    6. \u5982\u679c\u4f4e\u4e8e\u76ee\u6807\u4ef7\u683c\uff0c\u53d1\u9001\u901a\u77e5\n    `;\n\n    return await this.executeTask(task);\n  }\n\n  async extractProductInfo() {\n    \/\/ \u4f7f\u7528\u5927\u6a21\u578b\u667a\u80fd\u63d0\u53d6\u7ed3\u6784\u5316\u6570\u636e\n    const pageContent = await this.agent.page.content();\n\n    const extractionPrompt = `\n    \u4ece\u4ee5\u4e0bHTML\u4e2d\u63d0\u53d6\u5546\u54c1\u4fe1\u606f\uff08JSON\u683c\u5f0f\uff09\uff1a\n    - \u5546\u54c1\u540d\u79f0\n    - \u5f53\u524d\u4ef7\u683c\n    - \u539f\u4ef7\uff08\u5982\u679c\u6709\uff09\n    - \u5e93\u5b58\u72b6\u6001\n    - \u5546\u54c1\u8bc4\u5206\n\n    HTML\u5185\u5bb9\uff1a\n    ${pageContent.substring(0, 5000)}\n    `;\n\n    const response = await this.openai.chat.completions.create({\n      model: \"gpt-4\",\n      messages: &#91;\n        {\n          role: \"system\",\n          content: \"\u4f60\u662f\u4e00\u4e2a\u6570\u636e\u63d0\u53d6\u4e13\u5bb6\uff0c\u4ece\u7f51\u9875HTML\u4e2d\u63d0\u53d6\u7ed3\u6784\u5316\u6570\u636e\uff0c\u8fd4\u56deJSON\u683c\u5f0f\u3002\"\n        },\n        {\n          role: \"user\",\n          content: extractionPrompt\n        }\n      ],\n      response_format: { type: \"json_object\" }\n    });\n\n    return JSON.parse(response.choices&#91;0].message.content);\n  }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6848\u4f8b2\uff1a\u81ea\u52a8\u5316\u6d4b\u8bd5\u751f\u6210<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>class TestGeneratorAgent extends IntelligentWebAgent {\n  async generateTestScenario(featureDescription) {\n    const prompt = `\n    \u57fa\u4e8e\u4ee5\u4e0b\u529f\u80fd\u63cf\u8ff0\uff0c\u751f\u6210\u8be6\u7ec6\u7684\u6d4b\u8bd5\u573a\u666f\u548c\u5bf9\u5e94\u7684Puppeteer\u64cd\u4f5c\u6b65\u9aa4\uff1a\n\n    \u529f\u80fd\u63cf\u8ff0\uff1a${featureDescription}\n\n    \u8bf7\u751f\u6210\uff1a\n    1. \u6d4b\u8bd5\u7528\u4f8b\u5217\u8868\n    2. \u6bcf\u4e2a\u6d4b\u8bd5\u7528\u4f8b\u7684\u8be6\u7ec6\u64cd\u4f5c\u6b65\u9aa4\n    3. \u9884\u671f\u7684\u9a8c\u8bc1\u70b9\n    `;\n\n    const testPlan = await this.openai.chat.completions.create({\n      model: \"gpt-4\",\n      messages: &#91;{ role: \"user\", content: prompt }],\n      tools: puppeteerTools\n    });\n\n    \/\/ \u6267\u884c\u6d4b\u8bd5\u7528\u4f8b\n    return await this.executeTestPlan(testPlan);\n  }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6848\u4f8b3\uff1a\u5185\u5bb9\u6293\u53d6\u4e0e\u6458\u8981<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>class ContentScraperAgent extends IntelligentWebAgent {\n  async scrapeAndSummarize(url, summaryLength = 200) {\n    await this.agent.navigate_to_page(url);\n\n    \/\/ \u63d0\u53d6\u4e3b\u8981\u5185\u5bb9\n    const mainContent = await this.agent.page.evaluate(() =&gt; {\n      \/\/ \u79fb\u9664\u811a\u672c\u3001\u6837\u5f0f\u7b49\n      const scripts = document.querySelectorAll('script, style, nav, footer, header');\n      scripts.forEach(el =&gt; el.remove());\n\n      \/\/ \u63d0\u53d6\u6b63\u6587\u5185\u5bb9\n      const article = document.querySelector('article, main, .content, .post');\n      return article ? article.innerText : document.body.innerText;\n    });\n\n    \/\/ \u4f7f\u7528\u5927\u6a21\u578b\u751f\u6210\u6458\u8981\n    const summary = await this.openai.chat.completions.create({\n      model: \"gpt-4\",\n      messages: &#91;\n        {\n          role: \"system\",\n          content: \"\u4f60\u662f\u4e00\u4e2a\u5185\u5bb9\u6458\u8981\u4e13\u5bb6\uff0c\u751f\u6210\u51c6\u786e\u3001\u7b80\u6d01\u7684\u6587\u7ae0\u6458\u8981\u3002\"\n        },\n        {\n          role: \"user\",\n          content: `\u8bf7\u4e3a\u4ee5\u4e0b\u5185\u5bb9\u751f\u6210\u4e00\u4e2a${summaryLength}\u5b57\u5de6\u53f3\u7684\u6458\u8981\uff1a\\n\\n${mainContent.substring(0, 8000)}`\n        }\n      ]\n    });\n\n    return {\n      url: url,\n      content: mainContent,\n      summary: summary.choices&#91;0].message.content\n    };\n  }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u7b2c\u56db\u90e8\u5206\uff1a\u6700\u4f73\u5b9e\u8df5\u4e0e\u4f18\u5316<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">4.1 \u6027\u80fd\u4f18\u5316<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ 1. \u6d4f\u89c8\u5668\u5b9e\u4f8b\u590d\u7528\nclass BrowserPool {\n  constructor(poolSize = 3) {\n    this.pool = &#91;];\n    this.poolSize = poolSize;\n  }\n\n  async getBrowser() {\n    if (this.pool.length &gt; 0) {\n      return this.pool.pop();\n    }\n    return await puppeteer.launch({ headless: true });\n  }\n\n  async releaseBrowser(browser) {\n    if (this.pool.length &lt; this.poolSize) {\n      this.pool.push(browser);\n    } else {\n      await browser.close();\n    }\n  }\n}\n\n\/\/ 2. \u8bf7\u6c42\u62e6\u622a\u4f18\u5316\uff08\u7981\u7528\u4e0d\u5fc5\u8981\u7684\u8d44\u6e90\uff09\nasync function optimizePage(page) {\n  await page.setRequestInterception(true);\n  page.on('request', (req) =&gt; {\n    const resourceType = req.resourceType();\n    if (&#91;'image', 'stylesheet', 'font', 'media'].includes(resourceType)) {\n      req.abort();\n    } else {\n      req.continue();\n    }\n  });\n}\n\n\/\/ 3. \u5e76\u884c\u5904\u7406\nasync function parallelScraping(urls) {\n  const browser = await puppeteer.launch();\n  const results = await Promise.all(\n    urls.map(async (url) =&gt; {\n      const page = await browser.newPage();\n      await page.goto(url);\n      const title = await page.title();\n      await page.close();\n      return { url, title };\n    })\n  );\n  await browser.close();\n  return results;\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4.2 \u9519\u8bef\u5904\u7406\u7b56\u7565<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>class ErrorHandler {\n  static async handlePuppeteerError(error, context) {\n    const errorInfo = {\n      message: error.message,\n      stack: error.stack,\n      context: context,\n      timestamp: new Date().toISOString()\n    };\n\n    \/\/ \u6839\u636e\u9519\u8bef\u7c7b\u578b\u91c7\u53d6\u4e0d\u540c\u7b56\u7565\n    if (error.message.includes('Navigation timeout')) {\n      \/\/ \u5bfc\u822a\u8d85\u65f6\uff1a\u5c1d\u8bd5\u5237\u65b0\u6216\u4f7f\u7528\u66f4\u5bbd\u677e\u7684\u7b49\u5f85\u7b56\u7565\n      return {\n        action: 'retry',\n        strategy: 'use_networkidle0',\n        errorInfo\n      };\n    } else if (error.message.includes('Target closed')) {\n      \/\/ \u76ee\u6807\u5173\u95ed\uff1a\u91cd\u65b0\u521b\u5efa\u9875\u9762\n      return {\n        action: 'recreate_page',\n        errorInfo\n      };\n    } else if (error.message.includes('No node found')) {\n      \/\/ \u5143\u7d20\u672a\u627e\u5230\uff1a\u5c1d\u8bd5\u66ff\u4ee3\u9009\u62e9\u5668\n      return {\n        action: 'try_alternative_selector',\n        errorInfo\n      };\n    }\n\n    return {\n      action: 'log_and_continue',\n      errorInfo\n    };\n  }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4.3 \u5b89\u5168\u8003\u8651<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ 1. \u6c99\u7bb1\u9694\u79bb\nconst browser = await puppeteer.launch({\n  args: &#91;\n    '--no-sandbox',\n    '--disable-setuid-sandbox',\n    '--disable-dev-shm-usage',\n    '--disable-accelerated-2d-canvas',\n    '--disable-gpu',\n    '--disable-features=IsolateOrigins,site-per-process'\n  ]\n});\n\n\/\/ 2. \u5185\u5bb9\u5b89\u5168\u7b56\u7565\nawait page.setContent(html, {\n  waitUntil: 'networkidle0'\n});\n\n\/\/ 3. \u9650\u5236\u6267\u884c\u65f6\u95f4\nasync function executeWithTimeout(fn, timeout = 30000) {\n  return Promise.race(&#91;\n    fn(),\n    new Promise((_, reject) =&gt; \n      setTimeout(() =&gt; reject(new Error('\u64cd\u4f5c\u8d85\u65f6')), timeout)\n    )\n  ]);\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u603b\u7ed3<\/h2>\n\n\n\n<p>Puppeteer \u4e0e\u5927\u6a21\u578b\u7684\u7ed3\u5408\u4e3a\u6d4f\u89c8\u5668\u81ea\u52a8\u5316\u5e26\u6765\u65b0\u7684\u53ef\u80fd\u6027\u3002\u901a\u8fc7 Function Calling\uff0c\u5927\u6a21\u578b\u80fd\u591f\u7406\u89e3\u548c\u6267\u884c\u590d\u6742\u7684\u7f51\u9875\u64cd\u4f5c\uff0c\u800c Puppeteer \u63d0\u4f9b\u7a33\u5b9a\u7684\u6267\u884c\u73af\u5883\u3002<\/p>\n\n\n\n<p>\u5173\u952e\u6280\u672f\u70b9\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u6e05\u6670\u7684\u5de5\u5177\u51fd\u6570 Schema \u5b9a\u4e49<\/li>\n\n\n\n<li>\u53c2\u6570\u9a8c\u8bc1\u4e0e\u667a\u80fd\u8f6c\u6362<\/li>\n\n\n\n<li>\u9519\u8bef\u5904\u7406\u4e0e\u91cd\u8bd5\u673a\u5236<\/li>\n\n\n\n<li>\u89c6\u89c9\u7406\u89e3\u589e\u5f3a\u4ea4\u4e92\u80fd\u529b<\/li>\n\n\n\n<li>\u6027\u80fd\u4f18\u5316\u4e0e\u8d44\u6e90\u7ba1\u7406<\/li>\n<\/ol>\n\n\n\n<p>\u672a\u6765\u65b9\u5411\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u591a\u6a21\u6001\u7406\u89e3\uff08\u6587\u672c+\u89c6\u89c9\uff09<\/li>\n\n\n\n<li>\u66f4\u5f3a\u7684\u4e0a\u4e0b\u6587\u8bb0\u5fc6<\/li>\n\n\n\n<li>\u81ea\u4e3b\u9519\u8bef\u6062\u590d<\/li>\n\n\n\n<li>\u66f4\u81ea\u7136\u7684\u5bf9\u8bdd\u4ea4\u4e92<\/li>\n<\/ul>\n\n\n\n<p>\u8fd9\u79cd\u7ed3\u5408\u5c06\u63a8\u52a8\u667a\u80fd Agent \u5728\u81ea\u52a8\u5316\u9886\u57df\u7684\u5e94\u7528\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u524d\u8a00 \u5728 AI \u5feb\u901f\u53d1\u5c55\u7684\u80cc\u666f\u4e0b&#46;&#46;&#46;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-2576","post","type-post","status-publish","format-standard","hentry","category-4"],"_links":{"self":[{"href":"https:\/\/sanlangcode.com\/index.php\/wp-json\/wp\/v2\/posts\/2576","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sanlangcode.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sanlangcode.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sanlangcode.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sanlangcode.com\/index.php\/wp-json\/wp\/v2\/comments?post=2576"}],"version-history":[{"count":1,"href":"https:\/\/sanlangcode.com\/index.php\/wp-json\/wp\/v2\/posts\/2576\/revisions"}],"predecessor-version":[{"id":2577,"href":"https:\/\/sanlangcode.com\/index.php\/wp-json\/wp\/v2\/posts\/2576\/revisions\/2577"}],"wp:attachment":[{"href":"https:\/\/sanlangcode.com\/index.php\/wp-json\/wp\/v2\/media?parent=2576"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sanlangcode.com\/index.php\/wp-json\/wp\/v2\/categories?post=2576"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sanlangcode.com\/index.php\/wp-json\/wp\/v2\/tags?post=2576"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}