Puppeteer 用户交互 #

点击操作 #

基本点击 #

javascript
// 点击元素
await page.click('#button');

// 点击选择器
await page.click('.submit-button');

// 点击 XPath 元素
const [button] = await page.$x('//button[contains(text(), "提交")]');
await button.click();

点击选项 #

javascript
await page.click('#button', {
  button: 'left',        // 鼠标按钮: left, right, middle
  clickCount: 1,         // 点击次数: 1 单击, 2 双击
  delay: 100             // 点击延迟(毫秒)
});

// 双击
await page.click('#element', { clickCount: 2 });

// 右键点击
await page.click('#element', { button: 'right' });

等待后点击 #

javascript
// 等待元素可见后点击
await page.waitForSelector('#button', { visible: true });
await page.click('#button');

// 使用 Locator API(推荐)
await page.locator('#button').click();

点击隐藏元素 #

javascript
// 强制点击(忽略可见性检查)
await page.click('#hidden-button', { force: true });

// 使用 evaluate 触发点击
await page.evaluate(() => {
  document.querySelector('#hidden-button').click();
});

输入操作 #

文本输入 #

javascript
// 基本输入
await page.type('#username', 'john_doe');

// 带延迟输入(模拟真实打字)
await page.type('#username', 'john_doe', { delay: 100 });

// 清空后输入
await page.locator('#username').fill('new_value');

// 直接设置值(不触发事件)
await page.$eval('#username', el => el.value = 'direct_value');

输入方法对比 #

text
┌─────────────────────────────────────────────────────────────┐
│                    输入方法对比                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  page.type()      逐字符输入,触发 keydown/keyup/keypress    │
│                   适合需要触发输入事件的场景                   │
│                                                             │
│  locator.fill()   直接设置值,触发 input/change 事件          │
│                   推荐用于表单填充                            │
│                                                             │
│  $eval()          直接设置 value 属性                        │
│                   不触发任何事件                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

密码输入 #

javascript
// 输入密码
await page.type('#password', 'mySecretPassword', { delay: 50 });

// 使用 fill
await page.locator('#password').fill('mySecretPassword');

清除输入 #

javascript
// 方法一:使用 fill
await page.locator('#input').fill('');

// 方法二:使用键盘
await page.click('#input');
await page.keyboard.down('Control');
await page.keyboard.press('a');
await page.keyboard.up('Control');
await page.keyboard.press('Backspace');

// 方法三:使用 evaluate
await page.$eval('#input', el => el.value = '');

键盘操作 #

键盘按键 #

javascript
// 按下单个键
await page.keyboard.press('Enter');
await page.keyboard.press('Tab');
await page.keyboard.press('Escape');

// 按下并释放
await page.keyboard.down('Shift');
await page.keyboard.press('KeyA');
await page.keyboard.up('Shift');

// 组合键
await page.keyboard.down('Control');
await page.keyboard.press('a');
await page.keyboard.up('Control');

常用按键 #

javascript
// 功能键
await page.keyboard.press('Enter');
await page.keyboard.press('Tab');
await page.keyboard.press('Escape');
await page.keyboard.press('Backspace');
await page.keyboard.press('Delete');
await page.keyboard.press('Space');

// 方向键
await page.keyboard.press('ArrowUp');
await page.keyboard.press('ArrowDown');
await page.keyboard.press('ArrowLeft');
await page.keyboard.press('ArrowRight');

// 修饰键
await page.keyboard.press('Control');
await page.keyboard.press('Alt');
await page.keyboard.press('Shift');
await page.keyboard.press('Meta');  // Command (Mac) / Windows (Win)

快捷键组合 #

javascript
// Ctrl+A 全选
await page.keyboard.down('Control');
await page.keyboard.press('a');
await page.keyboard.up('Control');

// Ctrl+C 复制
await page.keyboard.down('Control');
await page.keyboard.press('c');
await page.keyboard.up('Control');

// Ctrl+V 粘贴
await page.keyboard.down('Control');
await page.keyboard.press('v');
await page.keyboard.up('Control');

// Ctrl+Enter 提交
await page.keyboard.down('Control');
await page.keyboard.press('Enter');
await page.keyboard.up('Control');

输入文本 #

javascript
// 直接输入文本
await page.keyboard.type('Hello World');

// 带延迟输入
await page.keyboard.type('Hello World', { delay: 100 });

发送字符 #

javascript
// 发送单个字符
await page.keyboard.sendCharacter('嗨');

// 区别:sendCharacter 触发 keypress,不触发 keydown/keyup

鼠标操作 #

鼠标移动 #

javascript
// 移动到指定位置
await page.mouse.move(100, 200);

// 带步数的移动(模拟真实轨迹)
await page.mouse.move(100, 200, { steps: 10 });

// 移动到元素
const element = await page.$('.target');
const box = await element.boundingBox();
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);

鼠标点击 #

javascript
// 单击
await page.mouse.click(100, 200);

// 双击
await page.mouse.click(100, 200, { clickCount: 2 });

// 右键点击
await page.mouse.click(100, 200, { button: 'right' });

// 带延迟点击
await page.mouse.click(100, 200, { delay: 100 });

鼠标按下和释放 #

javascript
// 按下
await page.mouse.down();

// 移动(拖拽)
await page.mouse.move(200, 300);

// 释放
await page.mouse.up();

拖拽操作 #

javascript
// 拖拽元素
const source = await page.$('.draggable');
const target = await page.$('.drop-zone');

const sourceBox = await source.boundingBox();
const targetBox = await target.boundingBox();

// 移动到源元素
await page.mouse.move(
  sourceBox.x + sourceBox.width / 2,
  sourceBox.y + sourceBox.height / 2
);

// 按下
await page.mouse.down();

// 移动到目标位置
await page.mouse.move(
  targetBox.x + targetBox.width / 2,
  targetBox.y + targetBox.height / 2,
  { steps: 20 }  // 分步移动,更真实
);

// 释放
await page.mouse.up();

滚动操作 #

javascript
// 使用 wheel 滚动
await page.mouse.wheel({ deltaY: 300 });

// 水平滚动
await page.mouse.wheel({ deltaX: 300 });

// 滚动到元素
await page.$eval('.target', element => {
  element.scrollIntoView({ behavior: 'smooth' });
});

// 滚动到页面底部
await page.evaluate(() => {
  window.scrollTo(0, document.body.scrollHeight);
});

选择操作 #

下拉选择 #

javascript
// 选择选项(按 value)
await page.select('#country', 'us');

// 选择选项(按文本)
await page.select('#country', 'United States');

// 多选
await page.select('#languages', 'en', 'zh', 'ja');

// 获取选中值
const selectedValue = await page.$eval('#country', el => el.value);

复选框操作 #

javascript
// 勾选复选框
await page.check('#agree');

// 取消勾选
await page.uncheck('#agree');

// 检查是否选中
const isChecked = await page.$eval('#agree', el => el.checked);

// 点击切换
await page.click('#checkbox');

单选框操作 #

javascript
// 选择单选项
await page.click('input[type="radio"][value="option1"]');

// 使用 check
await page.check('input[type="radio"][value="option1"]');

// 检查是否选中
const isSelected = await page.$eval(
  'input[type="radio"][value="option1"]',
  el => el.checked
);

文件上传 #

基本文件上传 #

javascript
// 方式一:使用 inputFile
const fileInput = await page.$('input[type="file"]');
await fileInput.uploadFile('./path/to/file.pdf');

// 方式二:使用 locator
await page.locator('input[type="file"]').setInputFiles('./path/to/file.pdf');

多文件上传 #

javascript
// 上传多个文件
await page.locator('input[type="file"]').setInputFiles([
  './file1.pdf',
  './file2.pdf',
  './file3.pdf'
]);

使用 Buffer 上传 #

javascript
const fs = require('fs');

// 从 Buffer 上传
const buffer = fs.readFileSync('./file.pdf');
await page.locator('input[type="file"]').setInputFiles({
  name: 'file.pdf',
  mimeType: 'application/pdf',
  buffer: buffer
});

拖拽上传 #

javascript
const fs = require('fs');

// 获取上传区域
const dropZone = await page.$('.drop-zone');
const box = await dropZone.boundingBox();

// 创建文件数据
const fileContent = fs.readFileSync('./file.pdf');

// 模拟拖拽上传
await page.evaluate((data) => {
  const dropZone = document.querySelector('.drop-zone');
  const file = new File([data.content], data.name, { type: data.type });
  const dataTransfer = new DataTransfer();
  dataTransfer.items.add(file);
  
  const dropEvent = new DropEvent('drop', {
    dataTransfer: dataTransfer
  });
  
  dropZone.dispatchEvent(dropEvent);
}, {
  content: fileContent,
  name: 'file.pdf',
  type: 'application/pdf'
});

高级交互 #

悬停操作 #

javascript
// 悬停在元素上
await page.hover('.menu-item');

// 等待下拉菜单出现
await page.waitForSelector('.dropdown-menu', { visible: true });

// 点击下拉菜单项
await page.click('.dropdown-menu .item');

焦点操作 #

javascript
// 聚焦元素
await page.focus('#input');

// 获取当前焦点元素
const focusedElement = await page.evaluateHandle(
  () => document.activeElement
);

滚动到元素 #

javascript
// 滚动到元素可见
await page.$eval('.target', el => {
  el.scrollIntoView({ behavior: 'smooth', block: 'center' });
});

// 使用 Locator API
await page.locator('.target').scrollIntoViewIfNeeded();

拖放操作 #

javascript
// 使用 HTML5 拖放 API
await page.evaluate(() => {
  const source = document.querySelector('.draggable');
  const target = document.querySelector('.drop-zone');
  
  const dataTransfer = new DataTransfer();
  
  source.dispatchEvent(new DragEvent('dragstart', { dataTransfer }));
  target.dispatchEvent(new DragEvent('dragover', { dataTransfer }));
  target.dispatchEvent(new DragEvent('drop', { dataTransfer }));
  source.dispatchEvent(new DragEvent('dragend', { dataTransfer }));
});

触摸操作 #

javascript
// 点击触摸
await page.tap('#button');

// 触摸滑动
await page.touchscreen.tap(100, 200);

// 模拟触摸事件
await page.evaluate(() => {
  const element = document.querySelector('.target');
  const touch = new Touch({
    identifier: 0,
    target: element,
    clientX: 100,
    clientY: 200
  });
  
  element.dispatchEvent(new TouchEvent('touchstart', {
    touches: [touch],
    bubbles: true
  }));
});

等待与同步 #

等待元素可交互 #

javascript
// 等待元素可见
await page.waitForSelector('#button', { visible: true });

// 等待元素可点击
await page.waitForSelector('#button', { visible: true, enabled: true });

// 使用 Locator API(自动等待)
await page.locator('#button').click();

等待动画完成 #

javascript
// 等待动画结束
await page.waitForFunction(
  selector => {
    const element = document.querySelector(selector);
    return element && window.getComputedStyle(element).animationPlayState === 'paused';
  },
  {},
  '.animated-element'
);

等待元素稳定 #

javascript
// 等待元素不再移动
let lastPosition;
let stableCount = 0;

while (stableCount < 3) {
  const box = await page.$eval('.element', el => {
    const rect = el.getBoundingClientRect();
    return { x: rect.x, y: rect.y };
  });
  
  if (lastPosition && box.x === lastPosition.x && box.y === lastPosition.y) {
    stableCount++;
  } else {
    stableCount = 0;
  }
  
  lastPosition = box;
  await page.waitForTimeout(100);
}

完整示例 #

登录流程自动化 #

javascript
const puppeteer = require('puppeteer');

async function login(url, credentials) {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  
  try {
    // 访问登录页
    await page.goto(url);
    
    // 等待登录表单加载
    await page.waitForSelector('#login-form');
    
    // 填写用户名
    await page.locator('#username').fill(credentials.username);
    
    // 填写密码
    await page.locator('#password').fill(credentials.password);
    
    // 勾选记住我
    await page.check('#remember-me');
    
    // 点击登录
    await page.click('#login-button');
    
    // 等待导航完成
    await page.waitForNavigation({ waitUntil: 'networkidle0' });
    
    // 验证登录成功
    const welcomeText = await page.$eval('.welcome', el => el.textContent);
    console.log('Login successful:', welcomeText);
    
    return true;
  } catch (error) {
    console.error('Login failed:', error);
    return false;
  } finally {
    await browser.close();
  }
}

login('https://example.com/login', {
  username: 'testuser',
  password: 'password123'
});

表单填写示例 #

javascript
const puppeteer = require('puppeteer');

async function fillForm(url, formData) {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  
  await page.goto(url);
  
  // 填写文本字段
  await page.locator('#name').fill(formData.name);
  await page.locator('#email').fill(formData.email);
  await page.locator('#phone').fill(formData.phone);
  
  // 选择下拉框
  await page.select('#country', formData.country);
  
  // 选择日期
  await page.locator('#birthdate').fill(formData.birthdate);
  
  // 选择性别
  await page.check(`input[name="gender"][value="${formData.gender}"]`);
  
  // 勾选复选框
  for (const interest of formData.interests) {
    await page.check(`input[name="interests"][value="${interest}"]`);
  }
  
  // 上传头像
  await page.locator('input[type="file"][name="avatar"]').setInputFiles(formData.avatar);
  
  // 填写文本区域
  await page.locator('#bio').fill(formData.bio);
  
  // 提交表单
  await page.click('#submit');
  
  // 等待成功消息
  await page.waitForSelector('.success-message');
  
  await browser.close();
}

fillForm('https://example.com/register', {
  name: 'John Doe',
  email: 'john@example.com',
  phone: '1234567890',
  country: 'US',
  birthdate: '1990-01-01',
  gender: 'male',
  interests: ['coding', 'music'],
  avatar: './avatar.jpg',
  bio: 'Hello, I am John!'
});

拖拽排序示例 #

javascript
const puppeteer = require('puppeteer');

async function dragAndDropSort(url) {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  
  await page.goto(url);
  
  // 获取所有可拖拽项
  const items = await page.$$('.sortable-item');
  
  // 将第一项拖到最后一项后面
  const firstItem = items[0];
  const lastItem = items[items.length - 1];
  
  const firstBox = await firstItem.boundingBox();
  const lastBox = await lastItem.boundingBox();
  
  // 移动到第一项
  await page.mouse.move(
    firstBox.x + firstBox.width / 2,
    firstBox.y + firstBox.height / 2
  );
  
  // 按下
  await page.mouse.down();
  
  // 移动到最后一项下方
  await page.mouse.move(
    lastBox.x + lastBox.width / 2,
    lastBox.y + lastBox.height + 10,
    { steps: 20 }
  );
  
  // 释放
  await page.mouse.up();
  
  await browser.close();
}

下一步 #

现在你已经掌握了 Puppeteer 的用户交互操作,接下来学习 JavaScript 执行与页面评估 了解如何在页面中执行 JavaScript!

最后更新:2026-03-28