chrome devtools protocol——Web 性能自动化
CharlesMQ 【转】chrome devtools protocol——Web 性能自动化 前言 在测试Web页面加载时间时,可能会是这样的: 打开chrome浏览器。 按F12打开开发者工具。 在浏览器上打开要测试的页面 查看开发者工具中Network面板的页面性能数据并记录 或者在开发者工具中Console面
Charles-MQ 【转】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.newtab() tab.start() tab.Runtime.enable() tab.Page.navigate(url={你的页面地址}) # 设置等待页面加载完成的时间 tab.wait(10) # 运行js脚本 timingremoteobject = tab.Runtime.evaluate( expression='performance.timing' ) # 获取performance.timing结果数据 timingproperties = tab.Runtime.getProperties( objectId=timingremoteobject.get('result').get('objectId') ) timing = {} for item in timingproperties.get('result'): if item.get('value', {}).get('type') == 'number': timing[item.get('name')] = item.get('value').get('value') # 获取performance.getEntries()数据 entriesremoteobject = tab.Runtime.evaluate( expression='performance.getEntries()' ) entriesproperties = tab.Runtime.getProperties( objectId=entriesremoteobject.get('result').get('objectId') ) entriesvalues = [] for item in entriesproperties.get('result'): if item.get('name').isdigit(): urltimingproperties = tab.Runtime.getProperties( objectId=item.get('value').get('objectId') ) entriesvalue = {} for sonitem in urltimingproperties.get('result'): if (sonitem.get('value', {}).get('type') == 'number'or sonitem.get('value', {}).get('type') == 'string'): entriesvalue[sonitem.get('name')] = sonitem.get('value').get('value') entriesvalues.append(entriesvalue) 获取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.requestdict = {} # 首个请求开始时间 self.start = None def requestwillbesent(self, **kwargs): if self.start is None: self.start = time.time() dicthttp = { 'url':kwargs.get('request').get('url'), 'start':kwargs.get('timestamp') } self.requestdict[kwargs.get('requestId')]=dicthttp #print "loading:%s" % kwargs.get('request').get('url') def loadingfinished(self, **kwargs): # 服务器返回code 例如404也是finished self.requestdict[kwargs.get('requestId')]['end'] = kwargs.get('timestamp') self.requestdict[kwargs.get('requestId')]['size'] = kwargs.get('encodedDataLength') def responsereceived(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.requestdict[kwargs.get('requestId')]['end'] = kwargs.get('timestamp') self.requestdict[kwargs.get('requestId')]['errortext'] = kwargs.get('errorText') networkapi = NetworkAPIImplemention() browser = pychrome.Browser('http://127.0.0.1:%d' % 9222) tab = browser.newtab() # 绑定回调函数 tab.Network.requestWillBeSent = networkapi.requestwillbesent tab.Network.responseReceived = networkapi.responsereceived tab.Network.loadingFinished = networkapi.loadingfinished tab.Network.loadingFailed = networkapi.loadingfailed tab.start() tab.Network.enable() tab.Runtime.enable() # 是否禁用缓存 if disablecache: tab.Network.setCacheDisabled(cacheDisabled=True) tab.Page.navigate(url={你的页面地址}) tab.wait(10) tab.stop() self.browser.closetab(tab) # 获取的所有url详细信息 print networkapi.requestdict 监听页面事件 有时候特别是一些复杂的页面,页面依赖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()); }); }); """ customresult = 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的一幅图
Discussion
No comments yet