• selenium-webdriver 阿里云ARMS 自动化巡检


        很久没更新了,今天分享一篇关于做项目巡检的内容,这部分,前两天刚在公司做了部门分享,趁着劲还没过,发出来跟大家分享下。

    一、本地巡检实现

    1. Selenium Webdriver(SW) 简介

    Selenium Webdriver(以下简称SW) 是一个用于自动化 web 浏览器操作的工具。

    它提供了一组用于模拟用户在网页中的交互的 API,可以通过编程方式来控制浏览器并执行各种操作,例如点击、填写表单、提取数据等。

    SW 支持多种编程语言,包括 Java、Python、JavaScript(Node.js)等,本次分享我们主要关注 SW 的 JavaScript 版本。

    2. SW 能做什么

    • 自动化测试:Selenium Webdriver 可以通过编写代码来模拟用户在浏览器中的行为,例如点击链接、填写表单、提交数据、执行搜索等。它提供了对多种浏览器的支持,包括 Chrome、Firefox、Safari、Edge、Internet Explorer 等,可以用于编写和执行自动化测试用例,验证 Web 应用程序的功能和性能。

    • 浏览器自动化:Selenium Webdriver 可以以编程方式控制浏览器的行为和属性,例如打开指定的网址、最大化窗口、切换标签页、处理弹窗、截图等。它能够模拟真实用户在浏览器中的操作,并提供对 DOM 元素的定位和操作的方法。

    • 数据抓取:Selenium Webdriver 可以被用于抓取网页上的数据。通过模拟用户的交互操作,可以实现从网页上提取信息、进行表单填充和提交、模拟点击和滚动等操作,以便自动化地获取需要的数据。

    • 跨浏览器测试:Selenium Webdriver 提供了一致的 API,可以编写一套测试脚本并在不同的浏览器上运行,以验证 Web 应用程序在各个浏览器上的兼容性和一致性。

    3. SW 巡检

    这个是当前ARMS上最主要的监控指标,我们每天巡检要做的事,就是对这些数据做采集;同时对比过往指标,做异常分析、性能监控、用户行为分析等相关工作

    3.1 浏览器相关

    3.1.1 浏览器驱动

    浏览器

    驱动

    chrome

    chromedriver.exe

    Firefox

     geckodriver.exe

    IE

     IEDriverServer.exe

    3.1.2 浏览器相关配置
    1. const { Options } = require('selenium-webdriver/chrome')
    2. let options = new Options()
    3. options.addArguments('--headless')
    4. options.addArguments('--no-sandbox')
    5. options.addArguments('--disable-gpu-sandbox')
    6. options.addArguments('--disable-dev-shm-usage')
    7. options.addArguments('--disable-extensions')
    8. options.addArguments('--remote-debugging-port=9222')
    9. options.addArguments('--window-size=1920,1080');
    10. options.addArguments('--start-fullscreen')
    11. options.addArguments('disable-blink-features=AutomationControlled')

    说明

    参数

    描述

    --headless

    无界面模式,Linux 上运行时由于缺少图形界面,可以避免显示窗口相关问题

    --no-sandbox 

     禁用 Chrome 浏览器的沙盒模式,避免在无界面模式下的一些权限问题

    --disable-gpu-sandbox

    禁用 Chrome 浏览器的 GPU 沙盒,也是为了避免在无界面模式下出现问题

    --disable-dev-shm-usage

     禁用 Chrome 浏览器的 /dev/shm 临时文件系统的使用

    --disable-extensions

    禁用 Chrome 浏览器的扩展插件功能,提高运行效率

    --remote-debugging-port=9222

    启用 Chrome 浏览器的远程调试端口,允许远程调试协议与浏览器进行交互

    --window-size=1920,1080

     设置浏览器窗口大小

    --start-fullscreen

     全屏模式

    disable-blink-features=AutomationControlled

    禁用 AutomationControlled特性,以避免被浏览器检测到自动化程序的控制。

    3.2 巡检流程

    巡检基本流程

    1)登录                     输入账号、密码、滑动验证

    2)获取数据             读取DOM文本、模拟接口获取数据

    3)生成表格并发送  将数据转化成带样式的表格,通过企微机器人发送到微信群

    3.2.1 登录
    1. // 跳转登录页
    2. await driver.get('https://signin.aliyun.com/login.htm?callback=https%3A%2F%2Farms.console.aliyun.com%2Fretcode#/main')
    3. driver.manage()
    4. // 等待页面加载完成
    5. await driver.wait(async function () {
    6. let readyState = await driver.executeScript('return document.readyState')
    7. console.info('\x1B[37m ♫ Login readyState=' + readyState)
    8. return readyState === 'complete'
    9. }, 3000)
    10. await sleep(1000)
    11. // 输入用户名
    12. let inputEl = await driver.findElement(By.xpath('//input[@name="username"]'))
    13. await inputEl.clear();
    14. await sleep(100)
    15. await inputEl.sendKeys(USERNAME)
    16. await sleep(100)
    17. await (await driver.findElement(By.css('.next-col > button.next-btn-primary'))).click();
    18. // 等待
    19. await sleep(2000)
    20. await driver.wait(async function () {
    21. let readyState = await driver.executeScript('return document.readyState')
    22. return readyState === 'complete'
    23. }, 5000)
    24. // 输入密码
    25. let pwdInputEl = await driver.findElement(By.xpath('//input[@name="password"]'))
    26. await pwdInputEl.clear();
    27. await sleep(100)
    28. await pwdInputEl.sendKeys(PASSWORD)
    29. await sleep(3000)
    30. // 点击登录
    31. await (await driver.findElement(By.css('.next-col > button.next-btn-primary'))).click();
    32. await sleep(5000)
    33. url = await driver.getCurrentUrl()
    34. // 滑块验证
    35. if(url.indexOf('https://signin.aliyun.com/login.htm') === 0){
    36. slideVerify(driver,'#baxia-dialog-content','.sm-pop-inner .btn_slide');
    37. await sleep(5000)
    38. }

    3.2.2 获取数据
    1)读取DOM文本
    1. async function getPvData(driver, times = 0) {
    2. const resultMap = {}
    3. try {
    4. let pvSpan = await driver.findElement(By.css('div[data-e2e-id="summary-title-pv"] span.noclick-title'))
    5. let uvSpan = await driver.findElement(By.css('div[data-e2e-id="summary-title-uv"] span.noclick-title'))
    6. let jsErrorRate = await driver.findElement(By.css('span[data-e2e-id="card-content_title-jsErrorRate"]'))
    7. let apiSucRate = await driver.findElement(By.css('span[data-e2e-id="card-content_title-api"]'))
    8. let jsError = await driver.findElement(By.css('span[data-e2e-id="card-content_title-jsError'))
    9. let apiError = await driver.findElement(By.css('span[data-e2e-id="card-content_title-apiError"]'))
    10. let resourceError = await driver.findElement(By.css('span[data-e2e-id="card-content_title-resourceError"]'))
    11. const pvData = await pvSpan.getText()
    12. const jsErrorRateData = await jsErrorRate.getText()
    13. const apiSucRateData = await apiSucRate.getText()
    14. const jsErrorData = await jsError.getText()
    15. const apiErrorData = await apiError.getText()
    16. const uvData = await uvSpan.getText()
    17. const resourceErrorData = await resourceError.getText()
    18. if (!pvData || pvData.indexOf('--') > -1 || !jsErrorRateData || jsErrorRateData.indexOf('--') > -1 || !apiSucRateData || apiSucRateData.indexOf('--') > -1
    19. || !jsErrorData || jsErrorData.indexOf('--') > -1 || !uvData || uvData.indexOf('--') > -1) {
    20. if (times > 8) {
    21. return null
    22. }
    23. await sleep(3000)
    24. return await getPvData(driver, ++times)
    25. } else {
    26. resultMap.pv = pvData
    27. resultMap.jsErrorRate = jsErrorRateData
    28. resultMap.apiSucRate = apiSucRateData
    29. resultMap.jsError = jsErrorData
    30. resultMap.apiError = apiErrorData
    31. resultMap.uv = uvData
    32. resultMap.resourceError = resourceErrorData
    33. return resultMap
    34. }
    35. } catch (e) {
    36. if (times > 8) {
    37. return null
    38. }
    39. await sleep(3000)
    40. return await getPvData(driver, ++times)
    41. }
    42. }

    元素定位方法

    1. driver.findElement(By.css('.btn_slide'))
    2. driver.findElement(By.id("login_button"))
    3. driver.findElements(By.className('con_list_item'))
    4. driver.findElement(By.xpath('//input[@name="fm-login-id"]'))
    5. driver.executeScript('document.querySelector(\'.sts-loginout-wrap\').click()')

    2)模拟发起请求
    1. function requestWhiteScreen(driver, cookie, startDate, endDate, appId, page) {
    2. const intervalMillis = 2147483647,
    3. token = getQueryStr('XSRF-TOKEN', cookie.replace(/;/g, '&'))
    4. return driver.executeScript(`
    5. return window.fetch('https://arms.console.aliyun.com/api/retcode.json?action=RetcodeAction&eventSubmitDoGetDatas=1', {
    6. method: 'post',
    7. credentials: 'include',
    8. headers: {
    9. 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
    10. 'cookie': '${cookie}',
    11. 'X-XSRF-TOKEN': '${token}'
    12. },
    13. body: 'queryParams=%7B%22metric%22%3A%22webstat.sum%22%2C%22measures%22%3A%5B%22sum_val%22%5D%2C%22filters%22%3A%7B%22appId%22%3A${appId}%7D%2C%22dimensions%22%3A%5B%22key%22%5D%2C%22intervalMillis%22%3A${intervalMillis}%2C%22startTime%22%3A${startDate}%2C%22endTime%22%3A${endDate}%2C%22orderBy%22%3A%22sum_val%22%2C%22order%22%3A%22DESC%22%2C%22limit%22%3A%221000%22%7D'
    14. }).then(response => response.json())
    15. `)
    16. }

    3.2.3 生成巡检表格

    为了实现表格的良好视觉效果,采用了两个主要组件,分别是 xlsx 和 xlsx-style。

    xlsx                 数据整合成Excel表格

    xlsx-style        渲染表格样式

    xlsx 组件用于将数据整合成Excel表格的格式,而 xlsx-style 组件则提供了一系列丰富的表格样式,可以帮助我们实现带有样式的表格,从而提高表格的可读性和可视化效果。

    效果

    二、Linux 服务端部署

    1. 代码部署

    登录堡垒机

    1)同步代码:进入存放项目的目录,上传项目压缩文件,通过指令解压:unzip xx.zip

    2)安装依赖:npm i

    3)测试巡检:npm run xx

    2. 添加定时任务

    2.1 创建执行文件

    项目目录下,创建Shell脚本执行文件,如:h5-linux.sh

    说明

    1. killall -9 xx   结束其他关联进程

    2. npm run xx        执行巡检指令

    2.2 添加定时任务 

    1)查看当前的定时任务:终端窗口输入 crontab -l 指令

    crontab相关可参考:crontab使用方法

    2)添加定时任务:终端窗口输入 crontab -e 指令,进入编辑界面,添加定时任务(*最后一个用于测试)

    格式:

    *     *    *   *   *  command       分   时   日   月  周   命令

    说明

    示例    30 8 1,15,25 * * command   每月1、15、25日的 8 : 30 执行一次

    语法

     描述

    *

    代表所有可能的值,从左到右:分钟、小时、日期、月、星期几;

    其中分钟以 0-59、小时以1~23 (0表示0点) 、日期以 1-31 、月份以 1-12 、星期几以 0-6 的数字表示(0 表示星期日,1 表示星期一,以此类推)。 如每小时的第15分钟执行一次:      15 * * * * command

    ,

    可以用逗号隔开的值指定一个列表范围,例如:1,2,5,7,8,

    -

    可以用整数之间的中杠表示一个整数范围,例如 2-6 表示:2,3,4,5,6

    /

    用于指定时间区间,如:* 18-8/1 * * * command  晚上18点到早上8点之间,每隔一小时执行一次

    2.2 测试定时任务

    执行:./文件名.sh  或 sh 文件名.sh 运行任务

    3. 配置日志

    配置日志,用于记录巡检流程,确定问题的来源,并进行相关的故障排查。

    方法:进入编辑界面,在对应任务后面新增日志

    说明

    语法

    描述

     >> 目录/文件名.log

     日志输出目录/文件

    2>&1  

    是一种将标准错误输出(stderr)重定向到标准输出(stdout)的方法;确保即使发生错误,也能完整记录脚本的输出和错误信息。

    三、企微机器人

    1. 创建机器人

    企微群 (右上角) > ... > 添加群机器人 > 新创建一个机器人 > 输入机器人名称 > 添加机器人 

    2. 发送消息

    获取key:点击群机器人,从Webhook 地址获取到key

    2.1 发送文档

    1)文件上传:

    配置 formData,通过文件上传接口,将文档上传到文件系统,获取到 media_id(文件标识名)

    2)发送文件到企微群:

    通过配置消息类型文件标识名,使用发送接口,将文件发送到企微群中,并@对应负责人对异常指标进行分析

    1. const key = 'xx'
    2. async function sendWx(filePath) {
    3. const readStream = fs.createReadStream(filePath)
    4. const formData = new FormData()
    5. formData.append('media', readStream)
    6. const mediaId = await axios.post(`https://qyapi.weixin.qq.com/cgi-bin/webhook/upload_media?key=${key}&type=file`, formData)
    7. axios({
    8. url:`https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${key}`,
    9. method: 'post',
    10. headers: {
    11. 'Content-Type': 'application/json',
    12. },
    13. data: {
    14. msgtype: 'file',
    15. file: {
    16. 'media_id': mediaId?.data?.media_id||''
    17. }
    18. }
    19. }).then(() =>{
    20. fs.unlinkSync(filePath)
    21. console.info('\x1B[37m✔ 巡检已发送企微群\x1B[32m')
    22. const { mobile } = dutyArrangement()
    23. if(mobile){
    24. sentWxMsg('请项目负责人填写指标分析',mobile)
    25. }
    26. })
    27. }

    2.2 发送消息

    消息支持多种类型的配置,用户可以根据自身需求,选择不同的消息类型

    msgtype 支持类型

    • text                        文本消息
    • file                        文件消息
    • markdown                 Markdown 格式的消息
    • image                      图片消息
    • news                       新闻消息
    • template_card        模板卡片消息
    1. async function sentWxMsg(content,mentioned_mobile_list=[]){
    2. axios({
    3. url:`https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${key}`,
    4. method: 'post',
    5. headers: {
    6. 'Content-Type': 'application/json',
    7. },
    8. data: {
    9. msgtype: 'text',
    10. text: {
    11. content,
    12. mentioned_mobile_list,
    13. }
    14. }
    15. }).then(() =>{
    16. console.info('\x1B[37m✔ 巡检提示已发送企微群\x1B[32m')
    17. })
    18. }

    说明

    参数

    说明

    msgtype

      消息类型

    content

    消息内容

    mentioned_mobile_list

    手机号列表,提醒手机号对应的群成员(@某个成员),@all表示提醒所有人

     Markdown 格式的消息

    1. async function sentWxMarkdownMsg(content,tip){
    2. axios({
    3. url:`https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${key}`,
    4. method: 'post',
    5. headers: {
    6. 'Content-Type': 'application/json',
    7. },
    8. data: {
    9. msgtype: 'markdown',
    10. markdown: {
    11. content
    12. }
    13. }
    14. }).then(() =>{
    15. console.info(`\x1B[37m✔ ${tip}\x1B[32m`)
    16. })
    17. }
    18. const markdown = '昨天:ARMS--JS错误数:8,686,上周平均值为:2025.29'
    19. sentWxMarkdownMsg(markdown,'错误提示')

     *注意:markdown消息不支持@成员

    效果

    图文消息

    1. async function sentWxNewsMsg(news={},tip){
    2. axios({
    3. url:`https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${key}`,
    4. method: 'post',
    5. headers: {
    6. 'Content-Type': 'application/json',
    7. },
    8. data: {
    9. msgtype: 'news',
    10. news
    11. }
    12. }).then(() =>{
    13. console.info(`\x1B[37m✔ ${tip}\x1B[32m`)
    14. })
    15. }
    16. const news = {
    17. articles : [
    18. {
    19. title : '🌕月满情浓,好耶相送',
    20. description : '花好月圆人团圆,迎风赏月合家欢',
    21. url : 'https://www.baidu.com/',
    22. picurl:'https://i.hd-r.cn/317e50d14adb76e4e322df93a1da07b1.png'
    23. }
    24. ]
    25. }
    26. sentWxNewsMsg(news,'图文消息已发送')

    效果

    最后,佳节将至,祝大家:

    花好月圆人团圆,迎风赏月合家欢 ♥

  • 相关阅读:
    操作系统导论_第四章_抽象:进程
    FPGA之旅设计99例之第三例-----UART串口通信
    【Git】Git的基本概念,Git的基本操作
    Android原生插件开发-包名篇
    同步、异步
    [Js进阶]axios简要全局封装
    Kubernetes(29):Kubernetes Dashboard的使用
    【NI国产替代】高速数据采集模块,最大采样率为 125 Msps,支持 FPGA 定制化
    针对时间的功能测试点,这里给你总结全面了
    C语言的字符串处理函数
  • 原文地址:https://blog.csdn.net/sinat_36539161/article/details/133177230