【轉】chrome devtools protocol——Web 性能自動化


前言

在測試Web頁面加載時間時,可能會是這樣的:

  1. 打開chrome瀏覽器。
  2. 按F12打開開發者工具。
  3. 在瀏覽器上打開要測試的頁面
  4. 查看開發者工具中Network面板的頁面性能數據並記錄
  5. 或者在開發者工具中Console面板運行performance.timingperformance.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:

  1. Network.requestWillBeSent每個http請求發送前回調
  2. Network.responseReceived首次接送到http響應時回調
  3. Network.loadingFinished請求加載完成時回調
  4. 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的一幅圖

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM