Puppeteer 基础用法 #

浏览器生命周期 #

启动浏览器 #

javascript
const puppeteer = require('puppeteer');

// 基本启动
const browser = await puppeteer.launch();

// 带配置启动
const browser = await puppeteer.launch({
  headless: false,
  defaultViewport: { width: 1920, height: 1080 }
});

关闭浏览器 #

javascript
// 正常关闭
await browser.close();

// 断开连接(不关闭浏览器)
const wsEndpoint = browser.wsEndpoint();
await browser.disconnect();

// 重新连接
const browser = await puppeteer.connect({
  browserWSEndpoint: wsEndpoint
});

完整示例 #

javascript
const puppeteer = require('puppeteer');

async function main() {
  let browser;
  try {
    browser = await puppeteer.launch();
    const page = await browser.newPage();
    
    // 执行操作...
    await page.goto('https://example.com');
    
  } catch (error) {
    console.error('Error:', error);
  } finally {
    if (browser) {
      await browser.close();
    }
  }
}

main();

页面导航 #

基本导航 #

javascript
// 基本访问
await page.goto('https://example.com');

// 带选项访问
await page.goto('https://example.com', {
  timeout: 30000,           // 超时时间
  waitUntil: 'load'         // 等待条件
});

等待条件 #

text
┌─────────────────────────────────────────────────────────────┐
│                    waitUntil 选项说明                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  'load'           等待 load 事件触发(默认)                   │
│  'domcontentloaded' 等待 DOMContentLoaded 事件触发            │
│  'networkidle0'   等待 500ms 内无网络请求                     │
│  'networkidle2'   等待 500ms 内不超过 2 个网络请求             │
│  'commit'         收到响应头后立即返回                        │
│                                                             │
│  可以组合使用: ['load', 'networkidle0']                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

导航示例 #

javascript
// 等待页面完全加载
await page.goto('https://example.com', {
  waitUntil: 'load'
});

// 等待网络空闲
await page.goto('https://example.com', {
  waitUntil: 'networkidle0'
});

// 组合条件
await page.goto('https://example.com', {
  waitUntil: ['load', 'domcontentloaded']
});

// 快速返回(不等待资源加载)
await page.goto('https://example.com', {
  waitUntil: 'commit'
});

页面操作 #

javascript
// 后退
await page.goBack();

// 前进
await page.goForward();

// 刷新
await page.reload();

// 刷新带选项
await page.reload({
  waitUntil: 'networkidle0'
});

获取页面信息 #

javascript
// 获取 URL
const url = page.url();
console.log('Current URL:', url);

// 获取标题
const title = await page.title();
console.log('Page title:', title);

// 获取页面内容
const content = await page.content();
console.log('HTML content:', content);

元素选择器 #

CSS 选择器 #

javascript
// 选择单个元素
const element = await page.$('.my-class');
const button = await page.$('#submit-button');
const item = await page.$('div.container > ul > li:first-child');

// 选择多个元素
const elements = await page.$$('.my-class');
const links = await page.$$('a[href^="https"]');
const items = await page.$$('ul > li');

XPath 选择器 #

javascript
// 使用 XPath
const element = await page.$x('//div[@class="my-class"]');
const buttons = await page.$x('//button[contains(text(), "提交")]');

// XPath 返回数组
const [firstElement] = await page.$x('//h1');

文本选择器 #

javascript
// 精确文本匹配
const element = await page.locator('text=登录').element();

// 部分文本匹配
const element = await page.locator('text=/登录/i').element();

// 组合选择器
const element = await page.locator('button >> text=提交').element();

Locator API #

javascript
// 创建 Locator
const locator = page.locator('.my-element');

// 等待元素
await locator.waitFor();

// 点击元素
await locator.click();

// 填写内容
await locator.fill('Hello World');

// 获取元素
const element = await locator.element();
const elements = await locator.elements();

等待策略 #

waitForSelector #

等待元素出现或消失:

javascript
// 等待元素出现
await page.waitForSelector('.loading', { visible: true });

// 等待元素隐藏
await page.waitForSelector('.loading', { hidden: true });

// 等待元素从 DOM 移除
await page.waitForSelector('.loading', { hidden: true, timeout: 5000 });

waitForFunction #

等待自定义条件:

javascript
// 等待元素数量
await page.waitForFunction(
  () => document.querySelectorAll('.item').length >= 10
);

// 等待元素文本
await page.waitForFunction(
  (selector, text) => document.querySelector(selector)?.textContent.includes(text),
  {}, '.status', '完成'
);

// 等待全局变量
await page.waitForFunction(
  () => window.myData !== undefined
);

waitForTimeout #

固定时间等待(不推荐,仅用于调试):

javascript
// 等待 1 秒
await page.waitForTimeout(1000);

waitForNavigation #

等待页面导航完成:

javascript
// 点击后等待导航
await Promise.all([
  page.waitForNavigation(),
  page.click('a.link')
]);

// 等待特定 URL
await Promise.all([
  page.waitForNavigation({ url: '**/success' }),
  page.click('#submit')
]);

// 等待导航条件
await Promise.all([
  page.waitForNavigation({
    waitUntil: 'networkidle0',
    timeout: 60000
  }),
  page.click('#submit')
]);

waitForResponse #

等待网络响应:

javascript
// 等待特定请求响应
const response = await page.waitForResponse(
  response => response.url().includes('/api/data')
);
const data = await response.json();

// 点击触发请求
await Promise.all([
  page.waitForResponse(response => response.url().includes('/api/submit')),
  page.click('#submit')
]);

waitForRequest #

等待网络请求:

javascript
// 等待请求发出
const request = await page.waitForRequest(
  request => request.url().includes('/api/data')
);
console.log('Request:', request.url());

页面设置 #

视口设置 #

javascript
// 设置视口大小
await page.setViewport({
  width: 1920,
  height: 1080,
  deviceScaleFactor: 1,
  isMobile: false,
  hasTouch: false,
  isLandscape: false
});

// 获取当前视口
const viewport = page.viewport();
console.log(viewport);

模拟设备 #

javascript
const puppeteer = require('puppeteer');
const devices = puppeteer.devices;

// 使用预定义设备
const iPhone = devices['iPhone 13 Pro'];
await page.emulate(iPhone);

// 常用设备列表
const deviceNames = [
  'iPhone 13 Pro',
  'iPhone 13 Pro Max',
  'iPhone SE',
  'iPad Pro',
  'Pixel 5',
  'Galaxy S5',
  'Desktop Chrome',
  'Desktop Firefox'
];

User-Agent 设置 #

javascript
// 设置 User-Agent
await page.setUserAgent(
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
);

// 获取当前 User-Agent
const userAgent = await page.evaluate(
  () => navigator.userAgent
);
javascript
// 设置 Cookie
await page.setCookie({
  name: 'session',
  value: 'abc123',
  domain: 'example.com',
  path: '/',
  httpOnly: true,
  secure: true
});

// 获取 Cookie
const cookies = await page.cookies();
console.log(cookies);

// 获取特定域名的 Cookie
const cookies = await page.cookies('https://example.com');

// 删除 Cookie
await page.deleteCookie({ name: 'session' });

// 清除所有 Cookie
const client = await page.createCDPSession();
await client.send('Network.clearBrowserCookies');

本地存储 #

javascript
// 设置 localStorage
await page.evaluate(() => {
  localStorage.setItem('token', 'abc123');
  localStorage.setItem('user', JSON.stringify({ id: 1, name: 'John' }));
});

// 获取 localStorage
const token = await page.evaluate(
  () => localStorage.getItem('token')
);

// 清除 localStorage
await page.evaluate(() => localStorage.clear());

截图与 PDF #

截图 #

javascript
// 整页截图
await page.screenshot({
  path: 'screenshot.png'
});

// 全页面截图(包括滚动区域)
await page.screenshot({
  path: 'fullpage.png',
  fullPage: true
});

// 指定区域截图
await page.screenshot({
  path: 'clip.png',
  clip: {
    x: 0,
    y: 0,
    width: 800,
    height: 600
  }
});

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

// 返回 Base64
const base64 = await page.screenshot({
  encoding: 'base64'
});

// 返回 Buffer
const buffer = await page.screenshot();

PDF 生成 #

javascript
// 基本 PDF 生成
await page.pdf({
  path: 'document.pdf'
});

// 详细配置
await page.pdf({
  path: 'document.pdf',
  format: 'A4',
  printBackground: true,
  margin: {
    top: '20px',
    bottom: '20px',
    left: '20px',
    right: '20px'
  },
  scale: 1,
  displayHeaderFooter: true,
  headerTemplate: '<div style="text-align: center; width: 100%;">Header</div>',
  footerTemplate: '<div style="text-align: center; width: 100%;">Page <span class="pageNumber"></span></div>',
  preferCSSPageSize: false
});

// 横向 PDF
await page.pdf({
  path: 'landscape.pdf',
  format: 'A4',
  landscape: true
});

// 返回 Buffer
const buffer = await page.pdf();

页面内容获取 #

获取文本内容 #

javascript
// 获取单个元素文本
const text = await page.$eval('.title', el => el.textContent);
console.log(text);

// 获取多个元素文本
const texts = await page.$$eval('.item', elements => 
  elements.map(el => el.textContent)
);
console.log(texts);

获取属性值 #

javascript
// 获取属性
const href = await page.$eval('a.link', el => el.getAttribute('href'));
const src = await page.$eval('img', el => el.getAttribute('src'));

// 获取 data 属性
const dataId = await page.$eval('.item', el => el.dataset.id);

获取表单值 #

javascript
// 获取输入框值
const value = await page.$eval('#username', el => el.value);

// 获取选中状态
const checked = await page.$eval('#checkbox', el => el.checked);

// 获取下拉框选中值
const selected = await page.$eval('#select', el => el.value);

获取元素信息 #

javascript
// 获取元素边界
const boundingBox = await page.$eval('.card', el => {
  const rect = el.getBoundingClientRect();
  return {
    x: rect.x,
    y: rect.y,
    width: rect.width,
    height: rect.height
  };
});

// 检查元素是否可见
const isVisible = await page.$eval('.element', el => {
  const style = window.getComputedStyle(el);
  return style.display !== 'none' && 
         style.visibility !== 'hidden' &&
         style.opacity !== '0';
});

错误处理 #

Try-Catch 模式 #

javascript
async function safeClick(page, selector) {
  try {
    await page.waitForSelector(selector, { timeout: 5000 });
    await page.click(selector);
    return true;
  } catch (error) {
    console.error(`Failed to click ${selector}:`, error.message);
    return false;
  }
}

超时处理 #

javascript
// 设置默认超时
page.setDefaultTimeout(10000);  // 10 秒
page.setDefaultNavigationTimeout(30000);  // 30 秒

// 单独设置超时
await page.goto('https://example.com', {
  timeout: 60000
});

元素存在检查 #

javascript
// 检查元素是否存在
async function elementExists(page, selector) {
  const element = await page.$(selector);
  return element !== null;
}

// 使用示例
if (await elementExists(page, '.popup')) {
  await page.click('.popup .close');
}

完整示例 #

网页截图工具 #

javascript
const puppeteer = require('puppeteer');

async function screenshot(url, outputPath) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  try {
    await page.goto(url, { waitUntil: 'networkidle0' });
    await page.screenshot({ path: outputPath, fullPage: true });
    console.log(`Screenshot saved to ${outputPath}`);
  } catch (error) {
    console.error('Error:', error);
  } finally {
    await browser.close();
  }
}

screenshot('https://example.com', 'example.png');

数据抓取示例 #

javascript
const puppeteer = require('puppeteer');

async function scrapeData(url) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  try {
    await page.goto(url);
    await page.waitForSelector('.product-list');
    
    const products = await page.$$eval('.product', items => 
      items.map(item => ({
        name: item.querySelector('.name')?.textContent.trim(),
        price: item.querySelector('.price')?.textContent.trim(),
        image: item.querySelector('img')?.src
      }))
    );
    
    return products;
  } finally {
    await browser.close();
  }
}

scrapeData('https://example.com/products').then(console.log);

表单自动填充 #

javascript
const puppeteer = require('puppeteer');

async function fillForm(url, data) {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  
  await page.goto(url);
  
  // 填写表单
  await page.type('#name', data.name);
  await page.type('#email', data.email);
  await page.type('#phone', data.phone);
  await page.select('#country', data.country);
  
  // 勾选复选框
  const checkbox = await page.$('#agree');
  if (!(await checkbox.isChecked())) {
    await checkbox.click();
  }
  
  // 提交表单
  await page.click('#submit');
  
  // 等待结果
  await page.waitForSelector('.success-message');
  
  await browser.close();
}

fillForm('https://example.com/form', {
  name: 'John Doe',
  email: 'john@example.com',
  phone: '1234567890',
  country: 'US'
});

下一步 #

现在你已经掌握了 Puppeteer 的基础用法,接下来学习 用户交互 了解更多高级交互操作!

最后更新:2026-03-28