Puppeteer 教程展示了如何使用 puppeteer 库自动化浏览器。
人偶师
Puppeteer 是一个 Node 库,它提供高级 API 以通过 DevTools 协议控制 Chromium 或 Chrome。
Puppeteer 允许在无界面模式(默认模式)下使用浏览器,无需 UI。这非常适合编写脚本。
$ npm i puppeteer
我们使用npm i puppeteer 命令安装 Puppeteer。
Puppeteer 获取内容
在第一个示例中,我们获取页面的内容。
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 通过 launch 或 connect 函数连接到 Chromium 实例时,将创建一个浏览器。 launch 函数采用一组可选参数。默认情况下,浏览器以无头模式启动。
const page = await browser.newPage();
newPage 函数在默认浏览器上下文中创建一个新页面。
await page.goto('http://webcode.me');
使用goto,我们导航到给定的 URL。
const res = await page.content();
content 函数获取页面的完整 HTML 内容,包括文档类型。
await browser.close();
最后,我们关闭浏览器。
Puppeteer 创建屏幕截图
在下一个示例中,我们创建网页的屏幕截图。
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 文件。
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();
})();
例子生成一个PDF文件,使用page.pdf函数,我们选择a5格式。
Puppeteer 设备模拟
我们可以使用emulate 函数模拟特定设备。
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 在元素的中心单击。
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 应用程序。
<!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>
表单包含两个输入框:名称和消息。
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 应用程序处理已发布的表单。它根据发送的参数构建消息。
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('http://localhost: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 搜索引擎并处理结果。
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();
})();
该示例搜索猎鹰术语并返回 DuckDuckGo 的热门定义。
await page.goto('https://duckduckgo.com/', { waitUntil: 'networkidle2' });
我们导航到 DuckDuckGo;通过将waitUntil 选项设置为networkidle2,我们认为在至少 500 毫秒内不超过 2 个网络连接时导航完成。这是为了确保我们正确加载了页面,并且我们可以找到搜索框。
await page.type("input[id=search_form_input_homepage]", "falcon");
我们在 DuckDuckGo 的搜索框中输入猎鹰术语。
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__snippet 和js-result-snippet 类的标签。这是 DuckDuckGo 存储其定义的地方。
for (let e of res) {
     console.log(e.text);
}
我们检查定义并将它们打印到控制台。
在本文中,我们使用了 puppeteer 库来自动化浏览器。
列出所有 JavaScript 教程。

