ZetCode

Puppeteer 教程

最后修改于 2023 年 10 月 18 日

在本文中,我们将展示如何使用 puppeteer 库自动化浏览器。

Puppeteer

Puppeteer 是一个 Node 库,它提供了一个高级 API 来通过 DevTools 协议控制 Chromium 或 Chrome。

Puppeteer 允许在无头模式下使用浏览器(默认模式),无需 UI 即可工作。这对于脚本编写非常有用。

$ npm i puppeteer

我们使用 npm i puppeteer 命令安装 Puppeteer。

Puppeteer 获取内容

在第一个例子中,我们获取页面的内容。

app.js
const puppeteer = require('puppeteer');

(async () => {

    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('http://webcode.me');

    const res = await page.content();
    console.log(res);

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

该示例在无头模式下启动浏览器,转到 webcode.me 网页并检索其内容。

const puppeteer = require('puppeteer');

我们加载 puppeteer 库。

const browser = await puppeteer.launch();

当 Puppeteer 通过 launchconnect 函数连接到 Chromium 实例时,会创建一个浏览器。 launch 函数接受一组可选参数。 默认情况下,浏览器以无头模式启动。

const page = await browser.newPage();

newPage 函数在默认浏览器上下文中创建一个新页面。

await page.goto('http://webcode.me');

使用 goto,我们导航到给定的 URL。

const res = await page.content();

content 函数获取页面的完整 HTML 内容,包括 doctype。

await browser.close();

最后,我们关闭浏览器。

Puppeteer 创建屏幕截图

在下一个例子中,我们创建一个网页的屏幕截图。

app.js
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('http://webcode.me');
  await page.screenshot({ path: 'webcode.png' });

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

使用 page.screenshot 函数创建屏幕截图。

Puppeteer 创建 PDF 文件

在下面的例子中,我们从一个网页生成 PDF 文件。

app.js
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('http://webcode.me');
  await page.pdf({ path: 'webcode.pdf', format: 'a5' });

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

该示例使用 page.pdf 函数生成 PDF 文件。我们选择 a5 格式。

Puppeteer 设备模拟

我们可以使用 emulate 函数模拟特定设备。

app.js
const puppeteer = require('puppeteer');

(async () => {

  const options = {
    headless: false,
    slowMo: 100
  };

  const browser = await puppeteer.launch(options);
  const page = await browser.newPage();

  const device = puppeteer.devices['iPhone X'];
  await page.emulate(device);
  
  await page.goto('http://webcode.me');

  const res = await page.content()
  await page.waitForTimeout(5000);
  console.log(res);

  await browser.close()
})();

在示例中,我们模拟 iPhone 设备。

const options = {
     headless: false,
     slowMo: 100
};

const browser = await puppeteer.launch(options);

我们向 launch 函数传递一些选项。我们关闭无头模式并稍微放慢自动化速度。

const device = puppeteer.devices['iPhone X'];
await page.emulate(device);

我们选择一个设备并进行模拟。

const res = await page.content()
await page.waitForTimeout(5000);
console.log(res);

在我们输出网页内容之前,我们使用 waitForTimeout 暂停脚本的执行;休眠时间以毫秒为单位给出。

Puppeteer 链接点击

click 函数使用给定的选择器获取一个元素,如果需要,将其滚动到视图中,然后使用 page.mouse 在元素的中心单击。

app.js
const puppeteer = require('puppeteer');

const run = async () => {

    const options = { headless: false };
    const browser = await puppeteer.launch(options);
    const page = await browser.newPage();

    await page.goto('http://example.com');
    await page.waitForTimeout(3000);
    
    console.log(`Current page: ${page.url()}`);

    await page.click('a');

    console.log(`Current page: ${page.url()}`);

    await page.waitForTimeout(3000);

    return browser.close();
};

const logErrorAndExit = err => {
    console.log(err);
    process.exit();
};

run().catch(logErrorAndExit);

在示例中,我们导航到 example.com 页面,等待三秒钟,然后单击可用的链接。

Puppeteer 提交表单

在下面的例子中,我们提交一个 HTML 表单。我们使用 Express 库创建一个简单的 Web 应用程序。

public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Form</title>
</head>
<body>

    <form action="message" method="post">

        <div>
            <label>Name:</label>
            <input type="text" name="name">
        </div>

        <div>
            <label>Message</label>
            <input type="text" name="message">
        </div>

        <button type="submit">Send</button>

    </form>

</body>
</html>

该表单包含两个输入框:姓名和消息。

index.js
const express = require('express');
const path = require('path');

const app = express();
app.use(express.static('public'));
app.use(express.urlencoded({ extended: true }));

app.get('/', (req, res) => {

    res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

app.post('/message', (req, res) => {

    res.set({ 'Content-Type': 'text/plain; charset=utf-8' });

    let name = req.body.name;
    let message = req.body.message;
    let output = `${name} says: ${message}`;

    res.send(output);
});


app.listen(3000, () => {

    console.log('Application started on port 3000');
})

这个 Express 应用程序处理提交的表单。它根据发送的参数构建消息。

app.js
const puppeteer = require('puppeteer');

(async () => {

    const options = {
        headless: false,
        slowMo: 40
    };

    const browser = await puppeteer.launch(options);
    const page = await browser.newPage();
    await page.goto('https://:3000');

    await page.type('input[name="name"]', 'John Doe');
    await page.type('input[name="message"]', 'Hello there!');
    await page.click('button[type=submit]');

    const content = await page.content();

    if (content.includes('John Doe says: Hello there!')) {
        console.log('form submitted OK');
    } else {
        console.log('failed to submit form');
    }

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

在示例中,我们填写表单,提交它并验证响应。

const options = {
     headless: false,
     slowMo: 40
};

我们在 UI 模式下运行该示例;放慢一点。

await page.type('input[name="name"]', 'John Doe');
await page.type('input[name="message"]', 'Hello there!');

使用 page.type 函数,我们将数据填入输入标签。

await page.click('button[type=submit]');

我们使用 page.click 提交表单。

const content = await page.content();

if (content.includes('John Doe says: Hello there!')) {
     console.log('form submitted OK');
} else {
     console.log('failed to submit form');
}

我们验证来自 Express 应用程序的响应。

Puppeteer DuckDuckGo 搜索

在下面的例子中,我们将一个术语传递给 DuckDuckGo 搜索引擎并处理结果。

app.js
const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('https://duckduckgo.com/', { waitUntil: 'networkidle2' });

    await page.type("input[id=search_form_input_homepage]", "falcon");

    await Promise.all([
        page.click('input[type="submit"]'),
        page.waitForNavigation()
    ]);
    
    const res = await page.evaluate(() =>
        [...document.querySelectorAll(".result__snippet.js-result-snippet")].map(e => ({
            text: e.innerText
        })));

    for (let e of res) {

        console.log(e.text);
    }

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

该示例搜索 falcon 术语并返回 DuckDuckGo 的顶部定义。

await page.goto('https://duckduckgo.com/', { waitUntil: 'networkidle2' });

我们导航到 DuckDuckGo;通过将 waitUntil 选项设置为 networkidle2,我们认为导航完成,当网络连接不超过 2 个且持续至少 500 毫秒时。 这确保我们正确加载了页面,并且可以找到搜索框。

await page.type("input[id=search_form_input_homepage]", "falcon");

我们在 DuckDuckGo 的搜索框中输入 falcon 术语。

await Promise.all([
    page.click('input[type="submit"]'),
    page.waitForNavigation()
]);

我们提交表单并等待结果。

const res = await page.evaluate(() =>
    [...document.querySelectorAll(".result__snippet.js-result-snippet")].map(e => ({
        text: e.innerText
})));

我们在页面上下文中评估匿名函数。 在该函数中,我们选择同时具有 result__snippetjs-result-snippet 类的所有标签。 这是 DuckDuckGo 存储其定义的地方。

for (let e of res) {

     console.log(e.text);
}

我们遍历定义并将它们打印到控制台。

来源

Puppeteer 文档

在本文中,我们使用 puppeteer 库自动化浏览器。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我已经撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

查看 所有 JavaScript 教程。