Browser Automation

基于Puppeteer的网页数据抓取与浏览器自动化工具,支持动态内容提取和页面截图。

已扫描
适合谁
数据采集人员、自动化测试工程师
不适合谁
仅需静态页面数据的用户、有官方API接口但选择绕过者
国内可用性
需网络配置。可能需要网络配置或第三方服务可访问。
安装难度
新手友好(★☆☆)。基于终端操作、依赖、API Key 和本地环境要求的初步判断。

安装与下载

openclaw skills install @fasjdas/browser-automation-puppeteer

Skill 说明

命令、参数、文件名以原文为准

浏览器自动化

基于 Puppeteer 的网页抓取与浏览器自动化。

适用场景

建议使用此技能的情况:

  • “从 [网址] 抓取数据”
  • “提取 [产品/列表/项目] 的全部内容”
  • “对 [页面] 进行截图”
  • “爬取 [网站] 并收集 [信息]”
  • “填写并提交 [表单]”
  • 需要浏览器才能加载的 JavaScript 渲染内容

不建议使用此技能的情况:

  • 简单的静态页面 → 使用 web_fetch 更合适
  • 存在可用 API → 直接调用 API
  • 受限于频率限制的网站 → 尊重 robots.txt 规则

快速上手

# 安装 Puppeteer
npm install puppeteer

# 基础抓取示例
node scripts/scrape.js https://example.com

核心模式

启动浏览器

const puppeteer = require('puppeteer');

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

  const page = await browser.newPage();
  await page.goto(url, { waitUntil: 'networkidle2' });

  // ... 提取数据 ...

  await browser.close();
}

提取文本内容

// 获取选择器下的所有文本
const titles = await page.$$eval('h2', els => els.map(el => el.textContent.trim()));

// 获取单个元素的文本
const price = await page.$eval('.price', el => el.textContent.trim());

提取 HTML 内容

const html = await page.$eval('.product-list', el => el.innerHTML);

提取属性值

const links = await page.$$eval('a', els => els.map(el => ({
  text: el.textContent.trim(),
  href: el.getAttribute('href')
})));

等待内容加载

// 等待指定选择器出现
await page.waitForSelector('.results', { timeout: 10000 });

// 等待网络空闲(推荐)
await page.goto(url, { waitUntil: 'networkidle2' });

// 等待自定义函数返回 true
await page.waitForFunction(() => document.querySelectorAll('.item').length > 10);

分页处理

async function scrapeWithPagination(baseUrl, maxPages = 5) {
  const browser = await puppeteer.launch({ headless: 'new' });
  const page = await browser.newPage();

  let results = [];

  for (let i = 1; i <= maxPages; i++) {
    const url = `${baseUrl}?page=${i}`;
    await page.goto(url, { waitUntil: 'networkidle2' });

    const items = await page.$$eval('.item', els =>
      els.map(el => el.textContent.trim())
    );

    if (items.length === 0) break;
    results.push(...items);
  }

  await browser.close();
  return results;
}

截图功能

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

// 元素截图
const element = await page.$('.chart');
await element.screenshot({ path: 'chart.png' });

阻止资源加载(提升速度)

await page.setRequestInterception(true);
page.on('request', req => {
  if (['image', 'stylesheet', 'font'].includes(req.resourceType())) {
    req.abort();
  } else {
    req.continue();
  }
});

脚本示例

scrape.js —— 基础抓取脚本

// 使用方式:node scripts/scrape.js <url> [selector]
const puppeteer = require('puppeteer');

const url = process.argv[2];
const selector = process.argv[3] || 'body';

if (!url) {
  console.error('使用方式:node scrape.js <url> [selector]');
  process.exit(1);
}

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

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

  const content = await page.$$eval(selector, els =>
    els.map(el => el.textContent.trim())
  );

  console.log(JSON.stringify(content, null, 2));

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

screenshot.js —— 页面截图脚本

// 使用方式:node scripts/screenshot.js <url> [output.png]
const puppeteer = require('puppeteer');

const url = process.argv[2];
const output = process.argv[3] || 'screenshot.png';

if (!url) {
  console.error('使用方式:node screenshot.js <url> [output.png]');
  process.exit(1);
}

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

  await page.goto(url, { waitUntil: 'networkidle2' });
  await page.screenshot({ path: output, fullPage: true });

  console.log(`截图已保存至 ${output}`);
  await browser.close();
})();

crawl.js —— 多页爬虫脚本

// 使用方式:node crawl.js <url> <selector> [maxPages]
const puppeteer = require('puppeteer');

const url = process.argv[2];
const selector = process.argv[3];
const maxPages = parseInt(process.argv[4]) || 10;

if (!url || !selector) {
  console.error('使用方式:node crawl.js <url> <selector> [maxPages]');
  process.exit(1);
}

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

  let allData = [];

  for (let i = 1; i <= maxPages; i++) {
    const pageUrl = url.includes('?') ? `${url}&page=${i}` : `${url}?page=${i}`;
    console.error(`正在爬取:${pageUrl}`);

    await page.goto(pageUrl, { waitUntil: 'networkidle2' });

    const data = await page.$$eval(selector, els =>
      els.map(el => el.textContent.trim())
    );

    if (data.length === 0) break;
    allData.push(...data);
  }

  console.log(JSON.stringify(allData, null, 2));
  await browser.close();
})();

常见选择器

目标选择器
所有链接a
所有图片img
标题h1, h2, h3
列表项ul li, ol li
表格行table tr
卡片/项目.item, .card, .product
价格信息.price, [class*="price"]
描述内容.description, .summary

使用建议

  • 在爬取前检查 robots.txtcurl example.com/robots.txt
  • 在请求之间添加延迟,以避免被封禁:await new Promise(r => setTimeout(r, 2000))
  • **对于单页应用(SPAs)使用 networkidle2**
  • 当选择器失效时,通过截图进行调试
  • 设置用户代理,以应对屏蔽机器人访问的网站:
  await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');

参考

有关详细的 Puppeteer API 信息,请参阅 [puppeteer/docs/api.md](references/puppeteer-api.md)。

F
@fasjdas

已收录 1 个 Skill

相关推荐