【野路子实验室】移动端H5页面加载时间获取方法

本篇文章已经发表在了公众号上,这边再同步下。

======================================

先说两句

  1. 本文将介绍笔者近期实验的2种自动化获取移动端H5页面加载时间的方法,可能和大家平时一般使用的方式不同,在此抛砖引玉了。
  2. H5页面在Android Chrome浏览器中加载时,可以利用Chrome DevTools Protocol获取页面loading时长、所有加载的url、页面当前资源树等数据,也可以进行截图和模拟不同网络情况。
  3. 对于非Chrome非Native浏览器下,可以通过离线分析手机操作录屏,基于机器视觉进行网页动态加载过程的分析,获取每个加载事件的开始帧和结束帧,从而计算加载时间。

利用Chrome DevTools Protocol获取网页加载数据

哪来的路子

如果你有看过笔者上一篇文章《Web App新趋势——PWA》,那应该会对Lighthouse有了些许了解,它作为Chrome推出的自动化工具,可以针对页面运行一连串测试后生成页面性能报告。而它和大家熟悉不过的F12 – Chrome开发者工具一样,是通过Chrome DevTools Protocol来和目标页面通讯的。

该协议分为不同的功能模块域(domains),类似与Chrome开发者工具里的不同功能模块,常用API:

  • Network:网络请求、Cookie、缓存、证书等相关内容
  • Page:页面的加载、资源内容、弹层、截图、打印等相关内容
  • DOM:文档DOM的获取、修改、删除、查询等相关内容
  • Runtime:JavaScript代码的执行

Chrome DevTools Protocol是基于 WebScoket 协议来与页面建立通信通道,由发送给页面的Commands和它所产生的Events组成;每个 Command 包含 request 和 response 两部分,request 部分指定所要进行的操作以及操作说要的参数,response 部分表明操作状态,成功或失败。

但是一般不需要直接调用Websocket相关的内容来调用Chrome的调试命令,目前已有很多封装库,chrome-remote-interface – javascript是目前使用最广的,而本次实验将使用一个轻量的python封装库PyChromeDevTools

实验方案

本次实验将尝试利用CDP来获取Android WebView或Android Chrome中页面加载时间数据。

准备阶段


$ adb forward tcp:9222 localabstract:chrome_devtools_remote

若要获取APP的WebView中数据,需要将其配置为支持debugging:

// 支持Android 4.4及以上版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
MtcWebView.setWebContentsDebuggingEnabled(true);
}

并需要知道对应的pid:

Huis-MacBook:~ hui$ adb shell grep -a webview_devtools_remote /proc/net/unix
0000000000000000: 00000002 00000000 00010000 0001 01 569140 @webview_devtools_remote_17774
Huis-MacBook:~ hui$ adb forward tcp:9222 localabstract:webview_devtools_remote_17774

编写代码获取页面加载数据

  • 获取页面loading时间:

    import PyChromeDevTools
    import time
    chrome = PyChromeDevTools.ChromeInterface()
    chrome.Network.enable()
    chrome.Page.enable()
    start_time=time.time()
    chrome.Page.navigate(url="https://www.baidu.com/")
    chrome.wait_event("Page.loadEventFired", timeout=60)
    end_time=time.time()
    print ("Page Loading Time:", end_time-start_time)
    输出结果:
    >>> print ("Page Loading Time:", end_time-start_time) ('Page Loading Time:', 1.9704439640045166)
  • 可以选择带缓存和不带缓存的模式reload,可以用于测试多次打开页面的性能数据变化。
    >>> chrome.Page.reload(ignoreCache=True)
  • 获取所有页面加载的url:

    import PyChromeDevTools
    chrome = PyChromeDevTools.ChromeInterface()
    chrome.Network.enable()
    chrome.Page.enable()
    chrome.Page.navigate(url="http://cloud.baidu.com")
    event, messages = chrome.wait_event("Page.frameStoppedLoading", timeout=60)
    for m in messages:
    if "method" in m and m["method"] == "Network.responseReceived":
    try: url=m["params"]["response"]["url"]
    print (url)
    except: pass

    图片其中messages中是所有请求数据,且为json格式:
    图片
  • 获取当前页面资源树
    >>> chrome.Page.getResourceTree()
  • 可以类似在console控制台输入js命令,例如获取W3C的PerformanceTimingAPI的时间数据。
    >>> chrome.Runtime.evaluate(expression='window.performance.timing', generatePreview=True)

    可以配合Command Line API干更多事。
    获取带有指定的 CSS 选择器的第一个 DOM 元素的引用:

    >>> chrome.Runtime.evaluate(expression="$('img')",returnByValue=True, includeCommandLineAPI=True)

    简单alert(1):

    >>> expression = '$(document).ready(function() {alert(1)});'
    >>> print chrome.Runtime.evaluate(expression=expression, generatePreview=True, includeCommandLineAPI=True)

    屏幕缩放:

    >>> chrome.Runtime.evaluate(expression = "document.body.style.zoom=2")

结束测试清理

测试完毕后查看和清除adb forward:

Huis-MacBook:~ hui$ adb forward --list 0715f7bcb2711638 tcp:9222 localabstract:webview_devtools_remote_17774
Huis-MacBook:~ hui$ adb -s 0715f7bcb2711638 forward --remove tcp:9222
Huis-MacBook:~ hui$ adb forward --list

如果将WebView改为Chrome for Android,可以支持更多protocol,包括:

  • 截图:Page.captureScreenshot
  • 模拟不同网络环境:Network.emulateNetworkConditions

结论

优势:

  • 准确:这是 Webkit 内核反馈的数据,而不是外层 JavaScript 接口的统计,也不是通过代理监控网络数据拿到的结果。
  • 丰富:有很多数据,别的方法根本拿不到。例如,缓存状况、JavaScript 方法执行情况。
  • 标准:调试协议本身已经定义了大量的 JSON 数据结构,你不需要再次进行抽象设计。

劣势:

  • 若Chrome已经统治所有手机,那该方法基本不存在劣势;可惜目前中国Android端仍存在很多其他浏览器的拥趸者,而它们可没有都开源成这样。

基于机器视觉的网页动态加载过程的分析方法

哪来的路子

前一种方法目前只能获取H5页面在Chrome或WebView下打开时的加载时间,若需要在非Chrome非原生浏览器上进行时间获取呢?甚至是需要兼容iOS呢?

前几周在百度QA的公众号里看到了一篇文章,给出了基于帧计算的精确App响应时间计算的方案。主体思路是将操作App的视频离线处理为去重后的帧的数组,并展现在平台上供用户人为判定点选App开始启动和启动结束时间点的对应帧,从而计算出对应的耗时。而这个方法也可以用于H5页面加载时间的获取。

但是除了文章作者提出的执行时间相对较长的缺点以外,实验一个大于1分钟的视频,用该方法会产生大量截帧,用户浏览选择也会变得痛苦。

因此考虑将人为判定H5页面加载时间开始和结束的过程也用机器去解决。

实验思路

对不同机型、浏览器的使用观察,总结发现浏览器一般会包含一个进度条来标识网页的加载进度。进度条的出现和消失,也分别对应了网页的开始和结束加载两个时间点。因此通过对进度条的识别分析来确定加载时间,也符合用户对网页加载过程的实际视觉感受。

本实验的主要工作内容也就变成了通过机器视觉的方法来找到进度条的位置和状态,从而判定加载时间。

首个识别方案及效果

总结进度条视觉特点:

  1. 静态特征:一条水平的直线
  2. 运动特征:从左端开始出现,逐渐加长直到右端消失

单帧静态特征分析

  1. 录屏截帧
  2. 将图像转为灰度图
    cvtColor(image, I, CV_BGR2GRAY);
  3. 边缘检测

    blur(I, I, Size(3,3));
    Canny(I, contours, 100, 300, 3);
    threshold(contours, contours, 128, 255, THRESH_BINARY);
  4. 霍夫变换查找直线
    HoughLinesP(contours, lines, 1 , CV_PI/180, 80, 10, 10);
  5. 对找到的直线做进一步过滤,留下水平的且从最左端开始的直线,即为潜在的进度条

图片

多帧动态特征分析

思路:

  1. 在连续的多帧里,在row相同的位置出现的直线,它的col属性是随着帧数增加而逐步增加的。
  2. 进度条会持续出现在多个连续帧中,例如最少需要300ms
  3. 第一次出现时的直线长度要小于整个屏幕宽度的4/5
  4. 当这条直线消失时,它的最大长度与最开始出现时的最小长度的差值,需要超过屏幕宽度的1/5(过滤掉类似固定界面分割线)

图片

效果

该版本已经可以把提供的手机操作录屏中存在的所有加载事件段都找出来,但是仍存在以下不足:

  1. 仍存在非进度条的元素被误识别成进度条,甚至是肉眼观察非直线
  2. 对进度条的起点识别不够精确,例如进度条只有3个像素时,肉眼可以辨认是进度条,但是cv未识别。

持续改进方案

改进思路:人眼能确认3个像素的蓝点是进度条起始部分,因为人类根据经验知道这个位置的蓝色像素可能是进度条一部分。

为此,我们将识别过程分为两部分:

  1. 第一遍分析所有视频帧,学习确定进度条的大概位置(row值),进度条的backgroud color & foreground color的颜色范围。
  2. 第二遍分析所有视频帧,在可能的row值位置,根据颜色变化情况更为精确的确定进度条的起始和结束点。

特征提取

  1. 去黑边:因为存在录制视频像素与手机原屏幕像素的不匹配问题,导致视频帧有效范围左右两边可能存在黑边,影响检测。因此通过阈值检测方法确定左右黑边并去除。
  2. 类似最开始的识别方案中方法,找到可能的运动直线的位置,并对重复出现的位置信息做合并与计数,过滤掉干扰,并在此过程中计算进度条的颜色特征(RGB值范围)。

图片

精准识别

遍历所有视频帧,在第一遍找到的可能的progressbar的row值位置上,计算整个row的像素颜色特征,与之前提取的颜色匹配的,则认为是进度条。

效果

结果:

  1. 第一遍学习得到的row值较为精确,可以识别出随着网页的滚动导致的进度条在两个位置上轮流出现的情况。
    图片
  2. 基于颜色特征识别的方法可以识别到只有3个像素点的这种情况,识别到的进度条起始位置更为精准。下图中从左到右分别是进度条出现前一帧、进度条出现之只有3个像素、进度条最后一帧、进度条消失之后。

总结

识别精度:目前已有测试case下为100%识别,无误判无漏判,且开始与结束点精确到帧。
处理效率:1080p的mp4文件,i7单线程下,需要花费视频时长的1/5时间。
存在问题:因为基于录屏,因此精度受限于录屏帧率;此外该方法获取的加载时间为loading时长,可能仍存在异步请求未完成的情况,而非完全用户视角的可交互时间(Time to Interactive , TTI)。

最后小小的总结

本文介绍和实验了2种移动端H5页面加载时间的自动化获取方法,一种利用了Chrome DevTools Protocol,一种基于了机器视觉,前者精而专,后者泛而广,不知道是否能给大家带来一些新的思路?

【注】 野路子实验室,来源于笔者博而不精的常备武器库,惯于使用非常规方法去解决常规问题,旨在强身健体开阔思路。

发表评论