使用Puppeteer进行UI测试&爬取《ECMAScript 6 入门》

146 阅读5分钟

Puppeteer是由Google Chrome 官方团队进行维护的一个Node库,提供了一组高级API, 通过DevTools协议控制无界面Chrome (headless chrome, 也就是无UI界面的chrome)。

简而言之,Puppeteer可以让用户界面测试变得很轻松。

1、 使用Puppeteer进行UI测试

1.1、 什么是Puppeteer?

Puppeteer是一个用户界面自动化工具。 它组合了"Chrome无界面模式"和"DevTools协议",通过提供一个更上层的API,让用户界面测试自动化变得轻而易举。

无界面Chrome是没有UI界面的Chrome。 它允许你从浏览器之外的环境(即命令行)与Chromium进行交互。

1.2、 示例代码

ui-test-by-puppeteer-headless.png

示例代码的安装、执行

在安装依赖、执行示例代码之前,请先安装最新版的Node.js

# 建议使用cnpm进行安装,puppeteer需要下载Chromium (~170Mb Mac, ~282Mb Linux, ~280Mb Win)
sudo npm install -g cnpm

cnpm install

# 执行
npm start
# or npm run test

执行结果——无头

ui-test-by-puppeteer-headless.png

执行结果——有头

ui-test-by-puppeteer-running.png

代码说明

/**
* 下面是使用 puppeteer 的一个例子
*/
const puppeteer = require('puppeteer');

(async() => {
    // 1、打开 浏览器
    const browser = await puppeteer.launch({headless: true});
    // 2、打开 新页面
    const page = await browser.newPage();
    // 3、网址跳转到 百度
    await page.goto('<https://www.baidu.com>');
    // 4、在 百度的搜索框 输入 Puppeteer
    await page.type('#kw', 'Puppeteer', {delay: 50});
    // 5、点击 "百度一下" 按钮
    await page.click('#su');

    // 6、等待 1秒钟,等待百度传输结果
    await page.waitFor(1000);

    // 7、抽取所有结果的"标题"和"链接"
    const links = await page.evaluate(() => {
        const anchors = Array.from(document.querySelectorAll('.c-container h3.t a'));
        return anchors.map(anchor => anchor.textContent);
    });

    // 8、期待有结果
    console.log(links.join('\n'));

    // 9、关闭浏览器
    await browser.close();

})();

我们首先加载puppeteer模块——requireNode.js中用于加载模块的关键字。

然后我们将加载好的puppeteer模块放到名称为puppeteer的常量中—— constES6中用于定义常量的关键字

关于ES6,阮一峰写了一本免费、开源的电子书,点击这里查看

/**
* 下面是使用 puppeteer 的一个例子
*/
const puppeteer = require('puppeteer');

接下来是一个匿名的立即执行函数(IIFE,Immediately Invoked Function Expression),里面是我们需要执行的测试代码, asyncES7中用于异步操作的关键字,表示内部是异步操作,返回一个异步操作的结果。

关于asyncawait的更详细的内容,参见阮一峰的开源电子书《ECMAScript 6 入门》 (电子书地址:es6.ruanyifeng.com/

(async() => {

})();

1、打开 浏览器

// 1、打开 浏览器
const browser = await puppeteer.launch();

我们通过puppeteer模块提供的launch方法,打开一个浏览器。

在打开浏览器的同时,我们可以提供一些配置项,比如不使用无头浏览器(headless设置为false)。

awaitES7中用于异步操作的关键字,表示等待异步操作结束、得到返回的结果,await只能用在异步函数async function中。

打开浏览器Puppeteer来说是一个异步操作(返回值是Promise), Puppeteer的其他API也是类似处理。

我们为了模拟人的输入方式、或者为了查看方便,我们还可以设置slowMotrue,这样浏览器会模拟人的输入方式、缓慢地进行输入。

// 1、打开 浏览器
const browser = await puppeteer.launch({headless: true, slowMo: true});

2、打开 新页面

// 2、打开 新页面
const page = await browser.newPage();

3、网址跳转到 百度

// 3、网址跳转到 百度
await page.goto('<https://www.baidu.com>');

4、在 百度的搜索框 输入 Puppeteer

// 4、在 百度的搜索框 输入 Puppeteer
await page.type('#kw', 'Puppeteer', {delay: 50});

百度的关键字"输入框",是一个id属性为kw的'Input',我们要在里面输入Puppeteer字符串, 为了模拟人类的输入效果,各个字符之间、我们加了50毫秒的延迟。


<!-- 百度的搜索框,是一个`id`属性为`kw`的'Input' -->

<input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">

5、点击 "百度一下" 按钮

// 5、点击 "百度一下" 按钮
await page.click('#su');

百度的"百度一下"按钮,是一个id属性为su的'Submit Button'。


<!-- 百度的"百度一下"按钮,是一个`id`属性为`su`的'Submit Button'。 -->

<input type="submit" id="su" value="百度一下" class="bg s_btn">

6、等待 1秒钟,等待百度传输结果

// 6、等待 1秒钟,等待百度传输结果
await page.waitFor(1000);

由于"根据关键字进行查询"需要花一段时间,所以我们等1秒(1s,也就是1000毫秒,1000 ms)。

7、抽取所有结果的"标题"和"链接"

// 7、抽取所有结果的"标题"和"链接"
const links = await page.evaluate(() => {
const anchors = Array.from(document.querySelectorAll('.c-container h3.t a'));
  return anchors.map(anchor => anchor.textContent);
});

百度的搜索结果,是放在样式类为c-containerDiv中,我们想要的是结果是——结果的标题—— 这样我们只需要找到样式类为th3标签、然后找到里面的a标签,取它的文本内容。

baidu-screenshot.png

8、期待有结果

// 8、期待有结果
console.log(links.join('\n'));

9、关闭浏览器

// 9、关闭浏览器
await browser.close();

更多Puppeteer的API,请访问Puppeteer官方API地址

puppeteer-api-hierarchy.png

2、爬取《ECMAScript 6 入门》

使用Puppeteer爬取“阮一峰”开源电子书《ECMAScript 6 入门》(电子书地址:es6.ruanyifeng.com/)。

示例代码的仓库地址请点击这里

2.1 示例代码的安装、执行

# 建议使用cnpm进行安装,puppeteer需要下载Chromium (~170Mb Mac, ~282Mb Linux, ~280Mb Win)
sudo npm install -g cnpm

cnpm install

npm start

代码说明

/**
 * 使用Puppeteer爬取“阮一峰”开源电子书《ECMAScript 6 入门》(电子书地址:http://es6.ruanyifeng.com/)。
 */
const puppeteer = require('puppeteer');

(async() => {
    // 1、打开 浏览器
    const browser = await puppeteer.launch();
    // 2、打开 新页面
    let page = await browser.newPage();
    // 3、网址跳转到 电子书页面
    await page.goto('http://es6.ruanyifeng.com', {waitUtil: 'networkidle0'});
    await page.waitFor(2000);
    // 4、所有文章的链接地址
    let aTags = await page.evaluate(() => {
        let as = [...document.querySelectorAll('ol li a')];
        return as.map((a) =>{
            return {
                href: a.href.trim(),
                name: a.text
            };
        });
    });
    // 5、访问所有的文章,然后生成PDF
    for(let i = 0; i < aTags.length; i++) {
        page = await browser.newPage();
        await page.setViewport({width: 1200, height: 800});

        let a = aTags[i];
        await page.goto(a.href, {waitUtil: 'networkidle0'});
        await page.waitFor(5000);

        await page.pdf({path: `./docs/${i + '.' + a.name}.pdf`});

        await page.close();
    }

    // 6、关闭浏览器
    await browser.close();
})();

执行结果截图

es6-crawl-with-puppeteer.png