图片 1

Puppeteer——自动化脚本设计

我被分配了一个繁琐的任务,就是要给100个相同的站点做同样的配置。曾经就有做过相同的事,那时还不会写脚本,全靠手动配置。机械的配置了两天的时间,身体感觉被掏空。所以这次我决定还是写一个脚本自动的进行配置。

一、了解Puppeteer

中文版资料:https://juejin.im/entry/59ad6c4f5188250f4850dccc

官方文档(英文):https://github.com/GoogleChrome/puppeteer

Puppeteer的API(英文):https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md\#

 

二、环境

只安装了node环境

 

三、开发阶段

3.1 初始化项目

  引用了https://juejin.im/entry/59ad6c4f5188250f4850dccc

  项目都是以创建文件夹开始。

  $ mkdir thal
  $ cd thal

  初始化 NPM,填入一些必要的信息。

  $ npm init

 
安装 Puppeteer。由于 Puppeteer 并不是稳定的版本而且每天都在更新,所以如果你想要最新的功能可以直接通过
GitHub
仓库安装。

  $ npm i --save puppeteer

  Puppeteer 包含了自己的 chrome / chromium
用以确保可以无界面地工作。因此每当你安装/更新 puppeteer
的时候,他都会下载指定的 chrome 版本。

3.2 编码

3.2.1 工程的目录结构

node_modeles中的内容是从git上拉下来的,src文件夹写得是我自己的代码,我执行的是addConfig里面的文件,common中是一些基础性配置,contentHub配置内容较多,所以我另建了一个contentHub文件夹。

图片 1

 

3.2.2 common.js文件写的是常用的功能函数,这个是可以通用的。之前我不是说实现不了全选的功能吗,其实可以调用common中的setOption函数实现全选的功能

图片 2图片 3

 1 const config = require('./config');
 2 
 3 //根据选择器sel选择id为val的子项
 4 async function setOption(page, sel, val) {
 5   await page.evaluate((sel, val) => {
 6     document.querySelector(`${sel} > option[value="${val}"]`).selected = true;
 7     element = document.querySelector(sel);
 8     var event = new Event('change', { bubbles: true });
 9     event.simulated = true;
10     element.dispatchEvent(event);
11   }, sel, val);
12 }
13 
14 //在id为sel的输入框中输入val
15 async function setTextVal(page, sel, val) {
16   await page.evaluate((sel, val) => {
17     document.querySelector(sel).value = val;
18     element = document.querySelector(sel);
19     var event = new Event('change', { bubbles: true });
20     event.simulated = true;
21     element.dispatchEvent(event);
22   }, sel, val);
23 }
24 
25 //判断选择器是否存在
26 async function isExist(page, selector) {
27   var is = await page.evaluate((sel) => {
28     const element = document.querySelector(sel);
29     if (!element) {
30       return false;
31     } else {
32       return true;
33     }
34   }, selector);
35 
36   return is;
37 }
38 
39 //导入单个配置
40 async function importSingleConfiguration(page, configType, configContent) {
41   const confirmBtn = 'input[value="Confirm"]';
42   const configTypeSel = '#edit-config-type';
43   await setOption(page, configTypeSel, configType);
44   await page.click('#edit-import');
45   await setTextVal(page, '#edit-import', configContent);
46   await page.click('#edit-submit');
47   await page.waitForNavigation();
48 
49   const is = await isExist(page, confirmBtn);
50   if (is) {
51     await page.click(confirmBtn);
52     await page.waitForNavigation();
53     await page.waitFor(3 * config.stepWait);
54   }
55 }
56 
57 //设置checkbox中子项的值
58 async function setCheckBoxVal(page, sel, val) {
59   await page.evaluate((sel, val) => {
60     document.querySelector(sel).checked = val;
61     element = document.querySelector(sel);
62     var event = new Event('change', { bubbles: true });
63     event.simulated = true;
64     element.dispatchEvent(event);
65   }, sel, val);
66 }
67 
68 module.exports = {
69   setOption: setOption,
70   setTextVal: setTextVal,
71   importSingleConfiguration: importSingleConfiguration,
72   isExist: isExist,
73   selectAll: selectAll,
74   setCheckBoxVal: setCheckBoxVal,
75 
76 }

View Code

 

3.2.3 config.js文件中申明了许多基础性配置

我要跳转的网站url,登录的用户名和密码,站内页面跳转的路径等信息都配置在这个文件里面

图片 4图片 5

const baseUrlArray = [
     {
         url: '',
         langcode: '',
     },
];
const baseUrl = baseUrlArray[0].url;

const getUrl = (index) => {
    const baseUrl = baseUrlArray[index].url;
    return {
        hubConnection: `${baseUrl}/example`,
    };
}

const getLangCode = (index) => {
    return baseUrlArray[index].langcode;
}

module.exports = {
    secondWait: 1000,
    stepWait: 5000,
    username: '',
    password: '',
    credentials: {
        username: '',
        password: '',
    },
    baseUrl: baseUrl,
    baseUrlArray: baseUrlArray,
    urls: getUrl(0),
    getUrl: getUrl,
    getLangCode: getLangCode,
}

View Code

 

3.2.4 login.js

图片 6图片 7

 1 const config = require('./config');
 2 
 3 async function login(page, url = null) {
 4 
 5   //fill authenticate user name and password
 6   await page.authenticate(config.credentials);
 7 
 8   // goto login page
 9   await page.goto(url ? url : config.urls.login);
10   const agreeButton = await page.$('#block-popup .btn');
11   await agreeButton.click();
12 
13   //fill admin user name and password
14   await page.focus('#edit-name');
15   await page.keyboard.type(config.username);
16   await page.focus('#edit-pass');
17   await page.keyboard.type(config.password);
18 
19   const inputElement = await page.$('#edit-submit');
20   await inputElement.click();
21 
22   await page.waitForNavigation();
23 }
24 
25 module.exports = login;

View Code

 

3.2.5 好了,登录成功了

 

四、收获

1.需要被其它页面引用的函数,常量必须要在module.exports={}中申明

2.headles: false是设置自动化操作是可视化的

3.在输入框中输入值并覆盖原有的值:

    4.调用其他页面函数的声明:const common =
require(‘./common’);

 

五、疑惑

1.关于全选的功能,puppeteer并不支持全选,虽然官方文档上面说了linux和windows支持全选,但是我的linux系统没有任何反应,估计并不支持。补充一下,我这里说的是实现不了ctrl+A的全选功能。