1. 程式人生 > >【轉】chrome devtools protocol——Web 效能自動化

【轉】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的pychrome

github地址,使用方法很簡單,直接看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的一幅圖