【转】chrome devtools protocol——Web 性能自动化
前言
在测试Web页面加载时间时,可能会是这样的:
- 打开chrome浏览器。
- 按F12打开开发者工具。
- 在浏览器上打开要测试的页面
- 查看开发者工具中Network面板的页面性能数据并记录
- 或者在开发者工具中Console面板运行
performance.timing
和performance.getEntries()
收集数据
performance相关信息看这里PerformanceTiming
几十上百个页面,每个版本都这样来,估计疯了,所以就想怎么把它做成自动化呢?
chrome devtools protocol
chrome devtools protocol允许第三方对基于chrome的web应用程序进行调试、分析等,它基于WebSocket,利用WebSocket建立连接DevTools和浏览器内核的快速数据通道。一句话,有了这个协议就可以自己开发工具获取chrome的数据
协议详细内容看这里chrome devtools protocol
目前已经有很多大神针对这个协议封装出不同语言(nodejs,python,java...)的库,详细信息看这里awesome-chrome-devtools
这边我选择的是python的pychromegithub地址,使用方法很简单,直接看github上它的Demo
这个库依赖websocket-client
获取performance api数据
这里使用Runtime Domain中运行JavaScript脚本的APIRuntime.evaluate
# 开始前先启动chrome,启动chrome必须带上参数`--remote-debugging-port=9222`开启远程调试否则无法与chrome交互
browser = pychrome.Browser('http://127.0.0.1:%d' % 9222)
tab = browser.new_tab()
tab.start()
tab.Runtime.enable()
tab.Page.navigate(url={你的页面地址})
# 设置等待页面加载完成的时间
tab.wait(10)
# 运行js脚本
timing_remote_object = tab.Runtime.evaluate(
expression='performance.timing'
)
# 获取performance.timing结果数据
timing_properties = tab.Runtime.getProperties(
objectId=timing_remote_object.get('result').get('objectId')
)
timing = {}
for item in timing_properties.get('result'):
if item.get('value', {}).get('type') == 'number':
timing[item.get('name')] = item.get('value').get('value')
# 获取performance.getEntries()数据
entries_remote_object = tab.Runtime.evaluate(
expression='performance.getEntries()'
)
entries_properties = tab.Runtime.getProperties(
objectId=entries_remote_object.get('result').get('objectId')
)
entries_values = []
for item in entries_properties.get('result'):
if item.get('name').isdigit():
url_timing_properties = tab.Runtime.getProperties(
objectId=item.get('value').get('objectId')
)
entries_value = {}
for son_item in url_timing_properties.get('result'):
if (son_item.get('value', {}).get('type') == 'number'or
son_item.get('value', {}).get('type') == 'string'):
entries_value[son_item.get('name')] = son_item.get('value').get('value')
entries_values.append(entries_value)
获取Network数据
实际上performance.getEntries()不会记录404的请求信息,另外当前页面通过js触发新html页面请求时它只会记录第一个页面的请求,在这些情况下就需要通过Network Domain的API来收集所有请求信息,先介绍用到的API:
Network.requestWillBeSent
每个http请求发送前回调Network.responseReceived
首次接送到http响应时回调Network.loadingFinished
请求加载完成时回调Network.loadingFailed
请求加载失败时回调# 封装上面4个事件对应的回调方法
class NetworkAPIImplemention(object): def __init__(self):
self.request_dict = {}
# 首个请求开始时间
self.start = None def request_will_be_sent(self, **kwargs):
if self.start is None:
self.start = time.time()
dict_http = {
'url':kwargs.get('request').get('url'),
'start':kwargs.get('timestamp')
}
self.request_dict[kwargs.get('requestId')]=dict_http
#print "loading:%s" % kwargs.get('request').get('url') def loading_finished(self, **kwargs):
# 服务器返回code 例如404也是finished
self.request_dict[kwargs.get('requestId')]['end'] = kwargs.get('timestamp')
self.request_dict[kwargs.get('requestId')]['size'] = kwargs.get('encodedDataLength') def response_received(self, **kwargs):
self.request_dict[kwargs.get('requestId')]['type'] = kwargs.get('type')
self.request_dict[kwargs.get('requestId')]['response'] = kwargs.get('response') def loading_failed(self, **kwargs):
self.request_dict[kwargs.get('requestId')]['end'] = kwargs.get('timestamp')
self.request_dict[kwargs.get('requestId')]['error_text'] = kwargs.get('errorText')
network_api = NetworkAPIImplemention()
browser = pychrome.Browser('http://127.0.0.1:%d' % 9222)
tab = browser.new_tab()
# 绑定回调函数
tab.Network.requestWillBeSent = network_api.request_will_be_sent
tab.Network.responseReceived = network_api.response_received
tab.Network.loadingFinished = network_api.loading_finished
tab.Network.loadingFailed = network_api.loading_failed
tab.start()
tab.Network.enable()
tab.Runtime.enable()
# 是否禁用缓存
if disable_cache:
tab.Network.setCacheDisabled(cacheDisabled=True)
tab.Page.navigate(url={你的页面地址})
tab.wait(10)
tab.stop()
self.browser.close_tab(tab)
# 获取的所有url详细信息
print network_api.request_dict
监听页面事件
有时候特别是一些复杂的页面,页面依赖js和后端资源数据,并不是通常意义上页面loadEventEnd事件触发完就表示页面加载完成,这种情况可能需要依赖开发打点。
这里以开发设计了一个Loaded
事件为例
# 具体事件注册方式和注册时机询问开发,所谓注册时机即要求在js对象生成后注册,我们项目中page是在一个js文件中声明的,需要等这个js文件请求完成后再注册
# 这边使用Promise方式,这种方式awaitPromise参数必须是True
js = """
new Promise((resolve, reject) => {
page.getController().getPageEvent().addEventListener("Loaded",
function(){
resolve(new Date().getTime());
});
});
"""
custom_result = tab.Runtime.evaluate(
expression=js,
awaitPromise=True,
timeout=timeout * 1000
)
print custom_result.get('result').get('value')
有个坑
peformance.now()
获取与chrome开发者工具协议一样类型的时间时,这个时间不准确,只好用new Date().getTime()
写在最后一开始是使用nodejs的chrome-remote-interface,但是发现Page.loadEventFired
回调后不会再记录请求,事实上有些页面仍然有请求没有完成,不懂是不是我使用姿势不对附赠W3C的一幅图
【转】chrome devtools protocol——Web 性能自动化的更多相关文章
- Chrome DevTools Protocol Viewer
Chrome DevTools Protocol Viewer awesome-chrome-devtools
- 使用Chrome DevTools的Timeline和Profiles提高Web应用程序的性能
来源: http://www.oschina.net/translate/performance-optimisation-with-timeline-profiles 我们都希望创建高性能的Web应 ...
- [转]Web性能监控自动化探索之路–初识WebPageTest
本文转自:http://www.webryan.net/2013/01/use-webpagetest-to-analyze-web-performance/ 无论是从Velocity 2012还是在 ...
- 使用Chrome DevTools的Timeline分析页面性能
随着webpage可以承载的表现形式更加多样化,通过webpage来实现更多交互功能,构建web应用程序已经成为很多产品的首要选择.这种方式拥有非常明显的优势:跨平台.开发便捷.便于部署和维护等等,但 ...
- Web 性能优化:Preload与Prefetch的使用及在 Chrome 中的优先级
摘要: 理解Preload与Prefetch. 原文:Web 性能优化:Preload,Prefetch的使用及在 Chrome 中的优先级 作者:前端小智 Fundebug经授权转载,版权归原作者所 ...
- [Forward]Improving Web App Performance With the Chrome DevTools Timeline and Profiles
Improving Web App Performance With the Chrome DevTools Timeline and Profiles We all want to create h ...
- Chrome DevTools & performance & keywords
Chrome DevTools & performance & keywords performance / 优化性能 https://developers.google.com/we ...
- Chrome DevTools 面板全攻略
李华西,微医云服务团队前端开发工程师,喜欢瞎折腾,典型猫奴 Console 面板 此章节请打开 devtools/console/console.html 一起食用 一方面用来记录页面在执行过程中的信 ...
- web性能优化——浏览器相关
简介 优化是一个持续的过程.所以尽可能的不要有人为的参与.所以能自动化的或者能从架构.框架级别解决的就最更高级别解决. 这样即能实现面对开发人员是透明的.不响应,又能确保所有资源都是被优化过的. 场景 ...
随机推荐
- <基础> PHP 进阶之 抽象类(abstract)、接口(interface)、Trait(特征)
抽象类 PHP 5 支持抽象类和抽象方法.定义为抽象的类不能被实例化. 抽象方法只能在抽象类中,抽象类中可以包含非抽象方法 被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现 继 ...
- PHP中的 抽象类(abstract class)和 接口(interface)
抽象类abstract class 1 .抽象类是指在 class 前加了 abstract 关键字且存在抽象方法(在类方法 function 关键字前加了 abstract 关键字)的类. 2 .抽 ...
- js 简单算法
1. 嵌套数组打平并且去重 ,,,,,,,,[,,,,[,,]]]; var obj = {};//去重标识 function test(data) { var newArr = []; for ( ...
- 小柒2012 / spring-boot-quartz
spring-boot-quartz 基于spring-boot+quartz的CRUD动态任务管理系统,适用于中小项目. 基于spring-boot 2.x +quartz 的CRUD任务管理系统: ...
- flash时间轴声音大小控制
A2时间轴声音大小控制: var sound:Sound = new Sound(); sound.setVolume(200); 把背景音乐放到一个影片剪辑里,剪辑起名 例如bgm_mc 声音模式为 ...
- JAVA_Package
Javaの名前空間の仕組みの1つにパッケージがあります.大規模開発では必須の概念です.また.他人の作ったコードの再利用という観点でも.パッケージを正しく活用する必要があります. ・完全修飾名:パッケー ...
- sourcetree 添加私钥
参考网址: https://www.jianshu.com/p/2a4a39e3704f
- python中函数基础
函数 什么是函数? 函数分为内置函数和自定义函数 定义:在程序中具备某一功能的工具.在使用之前需准备该工具(函数的定义),遇到应用场景拿来就用(后引用). 为什么要用函数? 1.代码冗余 程序组织结构 ...
- 一个docker镜像中的目录删除不了问题
在一个容器中,删除一个目录,失败: bash-4.2# pwd /home/zxcdn/ottcache/tomcat bash-4.2# uname -a Linux 3516b6c97679 -. ...
- SQL Server 优化---为什么索引视图(物化视图)需要with(noexpand)强制查询提示
本文出处:http://www.cnblogs.com/wy123/p/6694933.html 第一次通过索引视图优化SQL语句,以及遇到的一些问题,记录一下. 语句分析 最近开发递交过来一个查询统 ...