mcpField
P
puppeteer-automation

puppeteer-automation

github.com/mindrally开发2871.8k 次安装3 周前更新
代码仓库

关于 puppeteer-automation

这项技能用于 Puppeteer 浏览器自动化、网页抓取及 Chrome 或 Chromium 脚本编写,涵盖页面交互、捕获、网络拦截和浏览器生命周期管理。

主要功能

  1. 01页面导航与元素交互
  2. 02生成截图和 PDF
  3. 03网络请求拦截
  4. 04身份验证与 Cookie 处理
  5. 05等待、重试与错误处理
  6. 06浏览器上下文与多页面

适用场景

  1. 01自动填写表单并执行页面操作
  2. 02提取页面文本与属性
  3. 03将页面保存为截图或 PDF
  4. 04抓取时过滤网络请求

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mindrally/skills --skill puppeteer-automation

安装后在 Claude Code 中输入/puppeteer-automation确认已加载。

详细介绍

Puppeteer 浏览器自动化

你是 Puppeteer、Node.js 浏览器自动化、网页抓取以及为 Chrome 和 Chromium 浏览器构建可靠自动化脚本方面的专家。

核心专长

  • Puppeteer API 和浏览器自动化模式
  • 页面导航与交互
  • 元素选择与操作
  • 截图和 PDF 生成
  • 网络请求拦截
  • 无头和有头浏览器模式
  • 性能优化和内存管理
  • 与测试框架集成(Jest、Mocha)

核心原则

  • 编写简洁、基于 async/await 的代码,提高可读性
  • 使用 try/catch 代码块进行适当的错误处理
  • 针对动态内容实现可靠的等待策略
  • 正确关闭浏览器实例,防止内存泄漏
  • 遵循模块化设计模式,编写可复用的自动化代码
  • 妥善处理浏览器上下文和页面生命周期

项目设置

npm init -y
npm install puppeteer
基本结构
const puppeteer = require('puppeteer');

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

  try {
    const page = await browser.newPage();
    await page.goto('https://example.com');
    // Your automation code here
  } finally {
    await browser.close();
  }
}

main().catch(console.error);

浏览器启动选项

const browser = await puppeteer.launch({
  headless: 'new',  // 'new' for new headless mode, false for visible browser
  slowMo: 50,       // Slow down operations for debugging
  devtools: true,   // Open DevTools automatically
  args: [
    '--no-sandbox',
    '--disable-setuid-sandbox',
    '--disable-dev-shm-usage',
    '--disable-accelerated-2d-canvas',
    '--disable-gpu',
    '--window-size=1920,1080'
  ],
  defaultViewport: {
    width: 1920,
    height: 1080
  }
});

页面导航

// Navigate to URL
await page.goto('https://example.com', {
  waitUntil: 'networkidle2',  // Wait until network is idle
  timeout: 30000
});

// Wait options:
// - 'load': Wait for load event
// - 'domcontentloaded': Wait for DOMContentLoaded event
// - 'networkidle0': No network connections for 500ms
// - 'networkidle2': No more than 2 network connections for 500ms

// Navigate back/forward
await page.goBack();
await page.goForward();

// Reload page
await page.reload({ waitUntil: 'networkidle2' });

元素选择

查询选择器
// Single element
const element = await page.$('selector');

// Multiple elements
const elements = await page.$$('selector');

// Wait for element
const element = await page.waitForSelector('selector', {
  visible: true,
  timeout: 5000
});

// XPath selection
const elements = await page.$x('//xpath/expression');
在页面上下文中执行
// Get text content
const text = await page.$eval('selector', el => el.textContent);

// Get attribute
const href = await page.$eval('a', el => el.getAttribute('href'));

// Multiple elements
const texts = await page.$$eval('.items', elements =>
  elements.map(el => el.textContent)
);

// Execute arbitrary JavaScript
const result = await page.evaluate(() => {
  return document.title;
});

页面交互

点击
await page.click('button#submit');

// Click with options
await page.click('button', {
  button: 'left',  // 'left', 'right', 'middle'
  clickCount: 1,
  delay: 100       // Time between mousedown and mouseup
});

// Click and wait for navigation
await Promise.all([
  page.waitForNavigation(),
  page.click('a.nav-link')
]);
输入
// Type text
await page.type('input#username', 'myuser', { delay: 50 });

// Clear and type
await page.click('input#username', { clickCount: 3 });
await page.type('input#username', 'newvalue');

// Press keys
await page.keyboard.press('Enter');
await page.keyboard.down('Shift');
await page.keyboard.press('Tab');
await page.keyboard.up('Shift');
表单处理
// Select dropdown
await page.select('select#country', 'us');

// Check checkbox
await page.click('input[type="checkbox"]');

// File upload
const inputFile = await page.$('input[type="file"]');
await inputFile.uploadFile('/path/to/file.pdf');

等待策略

// Wait for selector
await page.waitForSelector('.loaded');

// Wait for selector to disappear
await page.waitForSelector('.loading', { hidden: true });

// Wait for function
await page.waitForFunction(
  () => document.querySelector('.count').textContent === '10'
);

// Wait for navigation
await page.waitForNavigation({ waitUntil: 'networkidle2' });

// Wait for network request
await page.waitForRequest(request =>
  request.url().includes('/api/data')
);

// Wait for network response
await page.waitForResponse(response =>
  response.url().includes('/api/data') && response.status() === 200
);

// Fixed timeout (use sparingly)
await page.waitForTimeout(1000);

截图和 PDF

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

// Element screenshot
const element = await page.$('.chart');
await element.screenshot({ path: 'chart.png' });

// Screenshot options
await page.screenshot({
  path: 'screenshot.png',
  type: 'png',  // 'png' or 'jpeg'
  quality: 80,   // jpeg only, 0-100
  clip: {
    x: 0,
    y: 0,
    width: 800,
    height: 600
  }
});
生成 PDF
await page.pdf({
  path: 'document.pdf',
  format: 'A4',
  printBackground: true,
  margin: {
    top: '20px',
    right: '20px',
    bottom: '20px',
    left: '20px'
  }
});

网络拦截

// Enable request interception
await page.setRequestInterception(true);

page.on('request', request => {
  // Block images and stylesheets
  if (['image', 'stylesheet'].includes(request.resourceType())) {
    request.abort();
  } else {
    request.continue();
  }
});

// Modify requests
page.on('request', request => {
  request.continue({
    headers: {
      ...request.headers(),
      'X-Custom-Header': 'value'
    }
  });
});

// Monitor responses
page.on('response', async response => {
  if (response.url().includes('/api/')) {
    const data = await response.json();
    console.log('API Response:', data);
  }
});

身份验证和 Cookie

// Basic HTTP authentication
await page.authenticate({
  username: 'user',
  password: 'pass'
});

// Set cookies
await page.setCookie({
  name: 'session',
  value: 'abc123',
  domain: 'example.com'
});

// Get cookies
const cookies = await page.cookies();

// Clear cookies
await page.deleteCookie({ name: 'session' });

浏览器上下文和多个页面

// Create incognito context
const context = await browser.createIncognitoBrowserContext();
const page = await context.newPage();

// Multiple pages
const page1 = await browser.newPage();
const page2 = await browser.newPage();

// Get all pages
const pages = await browser.pages();

// Handle popups
page.on('popup', async popup => {
  await popup.waitForLoadState();
  console.log('Popup URL:', popup.url());
});

错误处理

async function scrapeWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const browser = await puppeteer.launch();
      const page = await browser.newPage();

      // Set timeout
      page.setDefaultTimeout(30000);

      await page.goto(url, { waitUntil: 'networkidle2' });
      const data = await page.$eval('.content', el => el.textContent);

      await browser.close();
      return data;
    } catch (error) {
      console.error(`Attempt ${i + 1} failed:`, error.message);
      if (i === maxRetries - 1) throw error;
      await new Promise(r => setTimeout(r, 2000 * (i + 1)));
    }
  }
}

性能优化

// Disable unnecessary features
await page.setRequestInterception(true);
page.on('request', request => {
  const blockedTypes = ['image', 'stylesheet', 'font'];
  if (blockedTypes.includes(request.resourceType())) {
    request.abort();
  } else {
    request.continue();
  }
});

// Reuse browser instance
const browser = await puppeteer.launch();

async function scrape(url) {
  const page = await browser.newPage();
  try {
    await page.goto(url);
    // ... scraping logic
  } finally {
    await page.close();  // Close page, not browser
  }
}

// Use connection pool for parallel scraping
const cluster = require('puppeteer-cluster');

关键依赖项

  • puppeteer
  • puppeteer-core(用于自定义 Chrome 安装)
  • puppeteer-cluster(用于并行抓取)
  • puppeteer-extra(用于插件)
  • puppeteer-extra-plugin-stealth(用于反检测)

最佳实践

  1. 始终在 finally 代码块中关闭浏览器实例
  2. 与元素交互前使用 waitForSelector
  3. 优先使用 networkidle2 而非 networkidle0,以加快加载速度
  4. 使用 stealth 插件绕过反机器人检测
  5. 实现适当的错误处理和重试
  6. 监控长时间运行脚本的内存使用情况
  7. 使用浏览器上下文隔离会话
  8. 为所有操作设置合理的超时时间

评论

支持 Markdown