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文件夹。
3.2.2 common.js文件写的是常用的功能函数,这个是可以通用的。之前我不是说实现不了全选的功能吗,其实可以调用common中的setOption函数实现全选的功能
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 }
3.2.3 config.js文件中申明了许多基础性配置
我要跳转的网站url,登录的用户名和密码,站内页面跳转的路径等信息都配置在这个文件里面
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, }
3.2.4 login.js
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;
3.2.5 好了,登录成功了
四、收获
1.需要被其它页面引用的函数,常量必须要在module.exports={}中申明
2.headles: false是设置自动化操作是可视化的
3.在输入框中输入值并覆盖原有的值:
4.调用其他页面函数的声明:const common = require('./common');
五、疑惑
1.关于全选的功能,puppeteer并不支持全选,虽然官方文档上面说了linux和windows支持全选,但是我的linux系统没有任何反应,估计并不支持。补充一下,我这里说的是实现不了ctrl+A的全选功能。