Skip to main content

Playwright 自动化实战:让 AI 帮你操作浏览器

·1612 words·4 mins

痛点场景
#

你有没有过这种经历:

每天要打开十几个网站查数据,重复操作累死人。

想抓取某个网站的信息,但不会写爬虫。 需要定期截图监控页面变化,每次都手动操作。

这活本该自动化。

为什么选择 Playwright?
#

市面上的浏览器自动化工具有很多:

工具 优点 缺点
Selenium 老牌、生态好 配置复杂、速度慢
Puppeteer 只支持 Chrome 功能单一
Playwright 支持多浏览器、API 简洁、速度快 相对较新

我选 Playwright 的理由:

  • ✅ 支持 Chrome、Firefox、WebKit
  • ✅ API 简洁,上手快
  • ✅ 内置等待机制,不容易出错
  • ✅ 支持无头模式,适合服务器部署

环境配置
#

1. 安装 Node.js
#

# 检查是否已安装
node --version

# 如果没有,安装 Node.js 20+
# macOS
brew install node@20

# Linux
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y nodejs

2. 安装 Playwright
#

# 创建项目目录
mkdir playwright-demo && cd playwright-demo

# 初始化项目
npm init -y

# 安装 Playwright
npm install -D playwright

# 安装 Chromium 浏览器
npx playwright install chromium

3. 安装系统依赖(Linux)
#

# Playwright 会自动检测并安装所需依赖
npx playwright install-deps chromium

依赖列表(约 200MB):

  • libatk-1.0-0
  • libatk-bridge2.0-0
  • libdrm2
  • libxkbcommon0
  • libxcomposite1
  • …(约 30 个库)

安装完成后,环境就准备好了。

第一个脚本:截图
#

基础截图
#

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({ 
    headless: true  // 无头模式
  });
  
  const page = await browser.newPage();
  await page.goto('https://www.example.com');
  
  await page.screenshot({ 
    path: 'screenshot.png',
    fullPage: true  // 完整页面
  });
  
  await browser.close();
  console.log('截图完成!');
})();

指定 viewport 尺寸
#

const page = await browser.newPage({
  viewport: { width: 1280, height: 800 }
});

保存为 JPEG
#

await page.screenshot({ 
  path: 'screenshot.jpg',
  type: 'jpeg',
  quality: 90  // 质量 0-100
});

实战:访问小红书首页
#

const { chromium } = require('playwright');

async function screenshotWebsite(url, screenshotName) {
  const browser = await chromium.launch({
    headless: true,
    args: [
      '--no-sandbox',
      '--disable-setuid-sandbox',
      '--disable-dev-shm-usage',
      '--disable-gpu'
    ]
  });

  const context = await browser.newContext({
    viewport: { width: 1280, height: 800 },
    userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    locale: 'zh-CN'
  });

  const page = await context.newPage();

  try {
    console.log(`正在访问:${url}`);
    
    await page.goto(url, {
      waitUntil: 'domcontentloaded',
      timeout: 30000
    });

    // 等待页面稳定
    await page.waitForTimeout(3000);

    // 截图
    const screenshotPath = `/path/to/${screenshotName}`;
    await page.screenshot({ 
      path: screenshotPath,
      fullPage: true
    });

    console.log(`✅ 截图已保存:${screenshotPath}`);
    
    const title = await page.title();
    console.log(`页面标题:${title}`);
    
    return screenshotPath;

  } catch (error) {
    console.error('访问失败:', error.message);
    throw error;
  } finally {
    await browser.close();
  }
}

// 运行
screenshotWebsite('https://www.xiaohongshu.com', 'xiaohongshu-home.png');

运行结果

正在访问:https://www.xiaohongshu.com
✅ 截图已保存:/path/to/xiaohongshu-home.png
页面标题:小红书 - 你的生活兴趣社区

高级用法
#

1. 点击元素
#

// 点击按钮
await page.click('#submit-button');

// 点击链接
await page.click('a[href="/login"]');

// 等待后点击
await page.waitForSelector('#submit-button');
await page.click('#submit-button');

2. 填写表单
#

// 填写文本框
await page.fill('#username', 'myusername');
await page.fill('#password', 'mypassword');

// 选择下拉框
await page.selectOption('#country', 'CN');

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

// 提交表单
await page.click('#submit');

3. 等待元素
#

// 等待元素出现
await page.waitForSelector('.result');

// 等待元素可见
await page.waitForSelector('.result', { state: 'visible' });

// 等待元素消失
await page.waitForSelector('.loading', { state: 'hidden' });

// 等待特定时间
await page.waitForTimeout(2000);

4. 提取数据
#

const data = await page.evaluate(() => {
  const results = [];
  const items = document.querySelectorAll('.product-item');
  
  items.forEach(item => {
    results.push({
      title: item.querySelector('.title')?.textContent,
      price: item.querySelector('.price')?.textContent,
      link: item.querySelector('a')?.href
    });
  });
  
  return results;
});

console.log(data);

5. 处理弹窗
#

// 等待并关闭弹窗
page.on('dialog', async dialog => {
  console.log(`弹窗内容:${dialog.message()}`);
  await dialog.accept();  // 或 dialog.dismiss()
});

6. 多标签页
#

// 打开新标签
const newPage = await context.newPage();
await newPage.goto('https://example2.com');

// 切换标签
await page.bringToFront();

// 关闭标签
await newPage.close();

反爬应对
#

1. 设置 User-Agent
#

const context = await browser.newContext({
  userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
});

2. 设置语言和本地化
#

const context = await browser.newContext({
  locale: 'zh-CN',
  timezoneId: 'Asia/Shanghai'
});

3. 随机延迟
#

// 随机等待 1-3 秒
const randomDelay = Math.floor(Math.random() * 2000) + 1000;
await page.waitForTimeout(randomDelay);

4. 使用代理
#

const context = await browser.newContext({
  proxy: {
    server: 'http://proxy.example.com:8080',
    username: 'user',
    password: 'pass'
  }
});

常见问题
#

问题 1: 页面加载超时
#

错误

page.goto: Timeout 30000ms exceeded

解决

// 1. 延长超时时间
await page.goto(url, { timeout: 60000 });

// 2. 使用 domcontentloaded 而不是 networkidle
await page.goto(url, { waitUntil: 'domcontentloaded' });

// 3. 禁用部分功能加速
const browser = await chromium.launch({
  args: ['--disable-gpu', '--disable-dev-shm-usage']
});

问题 2: 元素找不到
#

错误

page.click: Timeout 30000ms exceeded

解决

// 1. 先等待元素出现
await page.waitForSelector('#button');
await page.click('#button');

// 2. 使用更精确的选择器
await page.click('button.submit-btn');

// 3. 检查元素是否在 iframe 中
const frame = page.frame({ name: 'iframe-name' });
await frame.click('#button');

问题 3: 系统依赖缺失
#

错误

error while loading shared libraries: libatk-1.0.so.0

解决

# 自动安装所有依赖
npx playwright install-deps chromium

成本对比
#

方案 时间成本 稳定性 学习曲线
手动操作 高(每天 30 分钟+)
找外包开发 中(一次性)
自己写 Playwright 低(一次性学习) 中(1-2 天上手)

下一步行动
#

  1. 安装 Playwrightnpm install -D playwright
  2. 运行第一个脚本:截图示例
  3. 尝试表单填写:找个需要登录的网站练习
  4. 学习数据抓取:用 page.evaluate() 提取信息

老黄的笔记:

我花了一下午配置好 Playwright 环境。

现在可以自动访问任意网站、截图、抓取数据。 原本需要手动操作的重复工作,现在一个脚本搞定。

这个投资回报率,值。⚙️