Open weoyk opened 2 weeks ago
最近我老板让我每周四统计一下小程序的性能数据,汇总到表格中,通过飞书表格来维护各个团队的小程序性能指标数据,比如:启动时间、首次渲染耗时、页面白屏占比等等。
这个工作在一定程度上是有意义的,因为通过这些指标可以复盘一个月或者两个月来,前端对小程序方面的优化是否对性能有提升,结合飞书表格,可以呈现一个趋势图,非常直观。
但是,有一个低效的统计问题,我无法接受,就是每周四我需要登录微信公众号平台,打开性能菜单,找到每一个指标,手动复制到表格中,这个过程大概每次耗时5分钟左右,时间虽然也不长,但是周复一周,每年如此,还是会觉得挺乏味。
于是,我想了很多骚主意,比如:能不能调用微信开放接口?能不能写个chrome插件?能不能用自动化工具?我尝试了每一个方案,最终决定使用 puppeteer自动化工具来实现。
统计小程序性能指标数据。
注:以上数字均为随机填写,并不代表标准性能数据。
最终通过自动化方案,实现数据抓取,并返回:
抓取到数据后,绘制小程序性能趋势图:
在小程序开发文档找到了一个接口:获取小程序性能数据,这个接口的调用也非常简单,只有一个api。
api
调用方式:
POST https://api.weixin.qq.com/wxa/business/performance/boot?access_token=ACCESS_TOKEN
使用问题
使用以后发现几个问题:access_token 和 返回数据。
access_token
返回数据
token
考虑到上面两个因素,决定暂缓使用API的方式。
API
思路是,打开浏览器,登录公众号平台,借助chrome插件来实现对指标数据的获取。
chrome
实现思路:
manifest.json
manifest_version
background
content_scripts
{ "name": "性能抓取插件", "version": "2.0.0", "description": "抓取微信数据", "manifest_version": 3, "icons": { "16": "images/icon.png", "48": "images/icon.png", "128": "images/icon.png" }, "action": { "default_icon": "images/icon.png", "default_title": "汽销", "default_popup": "index.html" }, "background": { "service_worker": "service-worker.js" }, "content_scripts":[ { "matches":["https://wedata.weixin.qq.com/*"], "js":["content-script.js"], "run_at":"document_end" } ], "permissions": [ "cookies", "tabs", "notifications", "storage" ], "host_permissions":[] }
background 是设置插件在后台运行,对于微信数据抓取,此处用不上。 content_scripts 用来匹配对应的网站,然后再run_at时机下,执行脚本。这个是高频功能。 permissions 用来设置插件用到的权限,比如:cookies、tabs、notifications等,此处也用不上。
run_at
permissions
cookies
tabs
notifications
content-script.js
此文件会在微信的性能页面打开后,内容加载完成时执行,我们可以通过传统的DOM API来获取数据,比如:
DOM API
使用问题:
在使用的过程中,同样发现了一些问题,因为微信性能页面,都是后端渲染的,比如性能报告、启动性能、网络性能、运行性能和体验分析他们都是点击以后,才会渲染页面,那就意味着,chrome插件有获取难度。
cookie
总结:考虑上面问题后,此方案也暂缓使用。
为什么会想到这个方案呢?人工获取数据,需要先登录,依次点击各个Tab页面,然后复制数据,这个过程刚好是puppeteer最擅长的,所以技术吻合,省时省力。
Tab
puppeteer
实现方案:
创建一个nestjs项目,安装puppeteer插件,除了扫码以外,其它都是自动化实现:自动点击菜单、自动抓取数据、自动截图、自动点击按钮、自动切换Tab、自动打开新窗口等。
nestjs
npm i -g @nestjs/cli $ nest new puppeteer
如果对于nestjs 不了解,可以去官网看一下文档,写几个Demo就能入门,实现这个插件实际上并不需要很了解nestjs,只是用它当做node服务而已,你也可以用Express或者koa去做。
Demo
node
Express
koa
pnpm add puppeteer
app.controller.ts
@Get() async getPerformanceData() { const browser = await puppeteer.launch({ headless: false, }); }
@Get() 是一个注解函数,表示这个方法是get请求。 headless设置为false表示启动的浏览器是能打开,看见的。如果是无头,则不会打开浏览器。
@Get()
get
headless
false
const page = await browser.newPage(); await page.goto('https://mp.weixin.qq.com/', { waitUntil: 'domcontentloaded', });
注:每一步的代码,都是接着上一步的代码编写的。
await page.setViewport({ width: 1400, height: 1200 });
await page.waitForFunction(() => { return document.querySelectorAll('.data-overview__title').length > 0; });
这里要注意,扫码只能用户来做,插件没办法实现扫码功能,而且微信公众平台,也无法用账号密码的方式来登录,大家可以去测试。
通过waitForFunction函数,可以实现异步等待,当用户扫码成功后,会进入到首页,我们只需要判断首页某一个节点存在就相当于扫码成功。
waitForFunction
await page.waitForSelector('dt[class="menu_title clickable menu_data"]>a'); await page.click('dt[class="menu_title clickable menu_data"]>a');
const newPagePromise: Promise<Page> = new Promise((resolve) => browser.on('targetcreated', (target) => resolve(target.page())), ); const newPage = await newPagePromise; await newPage.setViewport({ width: 1400, height: 2100 });
当模拟点击【统计】菜单时,微信会另开一个页面,上面代码中的page对象指的是第一个窗口,如果微信另开一个窗口,我们是无法使用的,所以必须一步接收一个新的窗口页面,当做上下文对象。
page
await newPage.waitForSelector('div[id="outer_view"]', { timeout: 5000, });
平台数据这个页面数据比较多,加载时间比较久,我们多等待一下。
await newPage.locator('div ::-p-text(性能质量)').click(); await newPage.locator('div ::-p-text(性能数据)').click(); await newPage.waitForSelector('.performance_evaluate'); await new Promise((resolve) => setTimeout(resolve, 3000)); await newPage.screenshot({ path: resolve(__dirname, `./首页性能报告.png`), type: 'png', fullPage: true, });
点击性能质量菜单,会展开性能数据菜单,再次点击,才会加载首页性能数据。通过screenshot可以对首页进行截图,中间有一个延迟3秒,也是为了等待页面加载完成,否则截图不全。
screenshot
const data = await newPage.evaluate(() => { const obj = {}; const nodes = document.querySelector( '.weui-desktop-popover__desc .row', ).childNodes; obj['性能评估'] = nodes[1].textContent.trim(); obj['启动耗时'] = nodes[3].textContent.trim(); obj['首次渲染耗时'] = nodes[4].textContent.trim(); obj['JS注入耗时'] = nodes[5].textContent.trim(); obj['代码包下载耗时'] = nodes[6].textContent.trim(); return obj; });
首页是包含各种指标数据的,我们通过变量保存并返回。
await newPage .locator('.board-nav-container__nav ::-p-text(运行性能)') .click(); await new Promise((resolve) => setTimeout(resolve, 3000)); await newPage.waitForSelector('span[data-short=切换耗时分析]'); await newPage.setViewport({ width: 1400, height: 5100 }); await newPage.screenshot({ path: resolve(__dirname, `./运行性能.png`), type: 'png', fullPage: true, }); const switchData = await newPage.evaluate(() => { const nodes = document.querySelectorAll('.num_1t7ooPGwJu'); return [nodes[0].textContent.trim(), nodes[2].textContent.trim()]; }); data['页面切换耗时'] = switchData[0]; data['切换较慢占比'] = switchData[1];
await newPage .locator('.board-nav-container__nav ::-p-text(体验分析)') .click(); await new Promise((resolve) => setTimeout(resolve, 3000)); const whiteData = await newPage.evaluate(() => { const nodes = document.querySelectorAll('.num_1t7ooPGwJu'); return nodes[1].textContent.trim(); }); data['页面进入白屏占比'] = whiteData; await newPage.setViewport({ width: 1400, height: 2400 }); await newPage.screenshot({ path: resolve(__dirname, `./体验分析.png`), type: 'png', fullPage: true, });
await newPage .locator('.board-nav-container__nav ::-p-text(启动性能)') .click(); await new Promise((resolve) => setTimeout(resolve, 5000)); const path = resolve(__dirname, `./启动性能.png`); await newPage.setViewport({ width: 1400, height: 8300, }); await newPage.screenshot({ path, type: 'png', fullPage: true, });
await browser.close(); return data;
通过上面一系列的操作,插件就自动完成了所有数据的抓取,并通过接口返回,我们只需要拷贝数据到表格即可,当然也可以进一步对接飞书表格,通过API完成读写,此处不再扩展了。
其实这个工作量并不大,每周也只是耽误5分钟,但是作为程序员,如果有更快的方式,岂不美哉。我们通过puppeteer插件结合nestjs,除了启动后的扫码以外,剩下的全程自动化实现。 既完成了工作,又学习了新技能。
感谢大家观看,我是河畔一角,一名普通的前端工程师。
背景
最近我老板让我每周四统计一下小程序的性能数据,汇总到表格中,通过飞书表格来维护各个团队的小程序性能指标数据,比如:启动时间、首次渲染耗时、页面白屏占比等等。
这个工作在一定程度上是有意义的,因为通过这些指标可以复盘一个月或者两个月来,前端对小程序方面的优化是否对性能有提升,结合飞书表格,可以呈现一个趋势图,非常直观。
但是,有一个低效的统计问题,我无法接受,就是每周四我需要登录微信公众号平台,打开性能菜单,找到每一个指标,手动复制到表格中,这个过程大概每次耗时5分钟左右,时间虽然也不长,但是周复一周,每年如此,还是会觉得挺乏味。
于是,我想了很多骚主意,比如:能不能调用微信开放接口?能不能写个chrome插件?能不能用自动化工具?我尝试了每一个方案,最终决定使用 puppeteer自动化工具来实现。
目标
统计小程序性能指标数据。
注:以上数字均为随机填写,并不代表标准性能数据。
最终通过自动化方案,实现数据抓取,并返回:
抓取到数据后,绘制小程序性能趋势图:
方案
微信开放接口
在小程序开发文档找到了一个接口:获取小程序性能数据,这个接口的调用也非常简单,只有一个
api
。调用方式:
使用问题
使用以后发现几个问题:
access_token
和返回数据
。access_token
为线上的token
,前端一般不太容易拿到,前端不能自己去实现,因为整个小程序只有一个access_token
,后端已经实现了,前端就不能再去生成,否则会引发线上问题。如果让后端写一个读取token
接口给前端,可能会涉及到安全风险。考虑到上面两个因素,决定暂缓使用
API
的方式。chrome 插件
思路是,打开浏览器,登录公众号平台,借助
chrome
插件来实现对指标数据的获取。实现思路:
manifest.json
文件。manifest_version
、background
、content_scripts
等。content-script.js
文件此文件会在微信的性能页面打开后,内容加载完成时执行,我们可以通过传统的
DOM API
来获取数据,比如:使用问题:
在使用的过程中,同样发现了一些问题,因为微信性能页面,都是后端渲染的,比如性能报告、启动性能、网络性能、运行性能和体验分析他们都是点击以后,才会渲染页面,那就意味着,chrome插件有获取难度。
content-script.js
无法获取内容。cookie
,也无法拿到明文数据。总结:考虑上面问题后,此方案也暂缓使用。
使用 puppeteer 自动化插件
为什么会想到这个方案呢?人工获取数据,需要先登录,依次点击各个
Tab
页面,然后复制数据,这个过程刚好是puppeteer
最擅长的,所以技术吻合,省时省力。实现方案:
创建一个nestjs项目,安装
puppeteer
插件,除了扫码以外,其它都是自动化实现:自动点击菜单、自动抓取数据、自动截图、自动点击按钮、自动切换Tab、自动打开新窗口等。nestjs
脚手架,并创建项目。puppeteer
插件。app.controller.ts
文件,创建有一个有头浏览器,并启动。@Get()
是一个注解函数,表示这个方法是get
请求。headless
设置为false
表示启动的浏览器是能打开,看见的。如果是无头,则不会打开浏览器。chrome
后,新建一个页面,跳转到微信公众号平台。注:每一步的代码,都是接着上一步的代码编写的。
这里要注意,扫码只能用户来做,插件没办法实现扫码功能,而且微信公众平台,也无法用账号密码的方式来登录,大家可以去测试。
通过
waitForFunction
函数,可以实现异步等待,当用户扫码成功后,会进入到首页,我们只需要判断首页某一个节点存在就相当于扫码成功。当模拟点击【统计】菜单时,微信会另开一个页面,上面代码中的
page
对象指的是第一个窗口,如果微信另开一个窗口,我们是无法使用的,所以必须一步接收一个新的窗口页面,当做上下文对象。平台数据这个页面数据比较多,加载时间比较久,我们多等待一下。
点击性能质量菜单,会展开性能数据菜单,再次点击,才会加载首页性能数据。通过
screenshot
可以对首页进行截图,中间有一个延迟3秒,也是为了等待页面加载完成,否则截图不全。首页是包含各种指标数据的,我们通过变量保存并返回。
通过上面一系列的操作,插件就自动完成了所有数据的抓取,并通过接口返回,我们只需要拷贝数据到表格即可,当然也可以进一步对接飞书表格,通过
API
完成读写,此处不再扩展了。总结
其实这个工作量并不大,每周也只是耽误5分钟,但是作为程序员,如果有更快的方式,岂不美哉。我们通过
puppeteer
插件结合nestjs
,除了启动后的扫码以外,剩下的全程自动化实现。 既完成了工作,又学习了新技能。感谢大家观看,我是河畔一角,一名普通的前端工程师。