埋點實現原理了解一下

埋點實現原理了解一下

來自專欄霸天的前端筆記4 人贊了文章

前言

埋點,是網站分析的一種常用的數據採集方法。我們主要用來採集用戶行為數據(例如頁面訪問路徑,點擊了什麼元素)進行數據分析,從而讓運營同學更加合理的安排運營計劃。現在市面上有很多第三方埋點服務商,百度統計,友盟,growingIO 等大家應該都不太陌生,大多情況下大家都只是使用,最近我研究了下 web 埋點,你要不要了解下。

現有埋點三大類型

用戶行為分析是一個大系統,一個典型的數據平台。由用戶數據採集,用戶行為建模分析,可視化報表展示幾個模塊構成。現有的埋點採集方案可以大致被分為三種,手動埋點,可視化埋點,無埋點

  1. 手動埋點

    手動代碼埋點比較常見,需要調用埋點的業務方在需要採集數據的地方調用埋點的方法。優點是流量可控,業務方可以根據需要在任意地點任意場景進行數據採集,採集信息也完全由業務方來控制。這樣的有點也帶來了一些弊端,需要業務方來寫死方法,如果採集方案變了,業務方也需要重新修改代碼,重新發布。
  2. 可視化埋點

    可是化埋點是近今年的埋點趨勢,很多大廠自己的數據埋點部門也都開始做這塊。優點是業務方工作量少,缺點則是技術上推廣和實現起來有點難(業務方前端代碼規範是個大前提)。阿里的活動頁很多都是運營通過可視化的界面拖拽配置實現,這些活動控制項元素都帶有唯一標識。通過埋點配置後台,將元素與要採集事件關聯起來,可以自動生成埋點代碼嵌入到頁面中。

  3. 無埋點

    無埋點則是前端自動採集全部事件,上報埋點數據,由後端來過濾和計算出有用的數據,優點是前端只要載入埋點腳本。缺點是流量和採集的數據過於龐大,伺服器性能壓力山大,主流的 GrowingIO 就是這種實現方案。

我們暫時放棄可視化埋點的實現,在 手動埋點無埋點 上進行了嘗試,為了便於描述,下文我會稱採集腳本為 SDK。

思考幾個問題

埋點開發需要考慮很多內容,貫穿著不輕易動手寫代碼的原則,我們在開發前先思考下面這幾個問題

  1. 我們要採集什麼內容,進行哪些採集介面的約定
  2. 業務方通過什麼方式來調用我們的採集腳本
  3. 手動埋點:SDK 需要封裝一個方法給業務方進行調用,傳參方式業務方可控
  4. 無埋點:考慮到數據量對於伺服器的壓力,我們需要對無埋點進行開關配置,可以配置進行哪些元素進行無埋點採集
  5. 用戶標識:遊客用戶和登錄用戶的採集數據怎麼進行區分關聯
  6. 設備Id:用戶通過瀏覽器來訪問 web 頁面,設備Id需要存儲在瀏覽器上,同一個用戶訪問不同的業務方網站,設備Id要保持一樣,怎麼實現
  7. 單頁面應用:現在流行的單頁面應用和普通 web 頁面的數據採集是否有差異
  8. 混合應用:app 與 h5 的混合應用我們要怎麼進行通訊

我們要採集什麼內容,進行哪些採集介面的約定

第一期我們先實現對 PV(即頁面瀏覽量或點擊量) 、UV(一天內同個訪客多次訪問) 、點擊量、用戶的訪問路徑的基礎指標的採集。精細化分析的流量轉化需要和業務相關,需要和數據分析方做約定,我們預留擴展。所以我們的採集介面需要進行以下的約定

{ "header":{ // HTTP 頭部 "X-Device-Id":" 550e8400-e29b-41d4-a716-446655440000", //設備ID,用來區分用戶設備 "X-Source-Url":"https://www.baidu.com/", //源地址,關聯用戶的整個操作流程,用於用戶行為路徑分析,例如登錄,到首頁,進入商品詳情,退出這一整個完整的路徑 "X-Current-Url":"", //當前地址,用戶行為發生的頁面 "X-User-Id":"",//用戶ID,統計登錄用戶行為 }, "body":[{ // HTTP Body體 "PageSessionID":"", //頁面標識ID,用來區分頁面事件,例如載入和離開我們會發兩個事件,這個標識可以讓我們知道這個事件是發生在一個頁面上 "Event":"loaded", //事件類型,區分用戶行為事件 "PageTitle": "埋點測試頁", //頁面標題,直觀看到用戶訪問頁面 "CurrentTime": 「1517798922201」, //事件發生的時間 "ExtraInfo": { } //擴展欄位,對具體業務分析的傳參 }]}

以上就是我們現在約定好了的通用的事件採集的介面,所傳的參數基本上會根據採集事件的不同而發生變化。但是在用戶的整一個訪問行為中,用戶的設備是不會變化的,如果你想採集設備信息可以重新約定一個介面,在整個採集開始之前發送設備信息,這樣可以避免在事件採集介面上重複採集固定數據。

{ "header":{ // HTTP 頭部 "X-Device-Id" :"550e8400-e29b-41d4-a716-446655440000" , // 設備id }, "body":{ // HTTP Body體 "DeviceType": "web" , //設備類型 "ScreenWide" : 768 , // 屏幕寬 "ScreenHigh": 1366 , // 屏幕高 "Language": "zh-cn" //語言 }}

業務方通過什麼方式來調用我們的採集腳本

埋點應該讓調用的業務方,儘可能少有工作量,最好是什麼都不用做,??,但是實現起來有點難額。我們採用的方案是讓業務方在代碼里通過 script 腳本來引用我們的 SDK ,業務方只要配置一些需要的參數進行埋點定製(??我們講到過的無埋點的流量控制),然後什麼都不做就可以進行基礎數據的採集。

(function() { var collect = document.createElement(script); collect.type = text/javascript; collect.async = true; collect.src = http://collect.trc.com/index.js; var s = document.getElementsByTagName(script)[0]; s.parentNode.insertBefore(collect, s); })();//用戶自定義要進行無埋點採集的元素,如果不進行無埋點採集,可以不配置 var _XT = []; _XT.push([Target,div]);

手動埋點:SDK

如果業務方需要採集更多業務定製的數據,可以調用我們暴露出的方法進行採集

//自定義事件 sdk.dispatch(customEvent,{extraInfo:自定義事件的額外信息})

遊客與用戶關聯

我們使用 userId 來做用戶標識,同一個設備的用戶,從遊客用戶切換到登錄用戶,如果我們要把他們關聯起來,需要有一個設備Id 做關聯

web 設備Id

用戶通過瀏覽器來訪問 web 頁面,設備Id需要存儲在瀏覽器上,同一個用戶訪問不同的業務方網站,設備Id要保持一樣。web 變數存儲,我們第一時間想到的就是 cookie,sessionStorage,localStorage,但是這3種存儲方式都和訪問資源的域名相關。我們總不能每次訪問一個網站就新建一個設備指紋吧,所以我們需要通過一個方法來跨域共享設備指紋

我們想到的方案是,通過嵌套 iframe 載入一個靜態頁面,在 iframe 上載入的域名上存儲設備id,通過跨域共享變數獲取設備id,共享變數的原理是採用了iframe 的 contentWindow通訊,通過 postMessage 獲取事件狀態,調用封裝好的回調函數進行數據處理具體的實現方式

//web 應用,通過嵌入 iframe 進行跨域 cookie 通訊,設置設備id, collect.setIframe = function () { var that = this var iframe = document.createElement(iframe) iframe.id = "frame", iframe.src = http://collectiframe.trc.com // 配置域名代理,目的是讓開發測試生產環境代碼一致 iframe.style.display=none //iframe 設置的目的是用來生成固定的設備id,不展示 document.body.appendChild(iframe) iframe.onload = function () { iframe.contentWindow.postMessage(loaded,*); } //監聽message事件,iframe 載入完成,獲取設備id ,進行相關的數據採集 helper.on(window,"message",function(event){ that.deviceId = event.data.deviceId if(event.data && event.data.type == loaded){ that.sendDevice(that.getDevice(), that.deviceUrl); setTimeout(function () { that.send(that.beforeload) that.send(that.loaded) },1000) } }) }

iframe 與 SDK 通訊

function receiveMessageFromIndex ( event ) { getDeviceInfo() // 獲取設備信息 var data = { deviceId: _deviceId, type:event.data } event.source.postMessage(data, *); // 將設備信息發送給 SDK}//監聽message事件if(window.addEventListener){ window.addEventListener("message", receiveMessageFromIndex, false);}else{ window.attachEvent("onmessage", receiveMessageFromIndex, false)

如果你想知道可以看我的另一篇博客 web 瀏覽器指紋跨域共享

單頁面應用:現在流行的單頁面應用和普通 web 頁面的數據採集是否有差異

我們知道單頁面應用都是無刷新的頁面載入,所以我們在頁面跳轉的處理和我們的普通的頁面會有所不同。單頁面應用的路由插件運用了 window 自帶的無刷新修改用戶瀏覽記錄的方法,pushState 和 replaceState。

window 的 history 對象 提供了兩個方法,能夠無刷新的修改用戶的瀏覽記錄,pushSate,和 replaceState,區別的 pushState 在用戶訪問頁面後面添加一個訪問記錄, replaceState 則是直接替換了當前訪問記錄,所以我們只要改寫 history 的方法,在方法執行前執行我們的採集方法就能實現對單頁面應用的頁面跳轉事件的採集了

// 改寫思路:拷貝 window 默認的 replaceState 函數,重寫 history.replaceState 在方法里插入我們的採集行為,在重寫的 replaceState 方法最後調用,window 默認的 replaceState 方法 collect = {} collect.onPushStateCallback : function(){} // 自定義的採集方法 (function(history){ var replaceState = history.replaceState; // 存儲原生 replaceState history.replaceState = function(state, param) { // 改寫 replaceState var url = arguments[2]; if (typeof collect.onPushStateCallback == "function") { collect.onPushStateCallback({state: state, param: param, url: url}); //自定義的採集行為方法 } return replaceState.apply(history, arguments); // 調用原生的 replaceState }; })(window.history);

這塊介紹起來也比較的複雜,如果你想了解更多,可以看我的另一篇博客你需要知道的單頁面路由實現原理

混合應用:app 與 h5 的混合應用我們要怎麼進行通訊

現在大部分的應用都不是純原生的應用, app 與 h5 的混合的應用是現在的一種主流。

純 web 數據採集我們考慮到前端存儲數據容易丟失,我們在每一次事件觸發的時候都用採集介面傳輸採集到的數據。考慮到現在很多用戶的手機會有流量管家的軟體監控,如果在 App 中 h5 還是採集到數據就傳輸給服務端,很有可能會讓流量管家檢測到,給用戶報警,從而使得用戶不再信任你的 App , 所以我們在用戶操作的時候將數據傳給 app 端,存儲到 app。用戶切換應用到後台的時候,通過 app 端的 SDK 打包傳輸到伺服器,我們給 app 提供的方法封裝了一個適配器

// app 與 h5 混合應用,直接將數信息發給 appcollect.saveEvent = function (jsonString) { collect.dcpDeviceType && setTimeout(function () { if(collect.dcpDeviceType==android){ android.saveEvent(jsonString) } else { window.webkit && window.webkit.messageHandlers ? window.webkit.messageHandlers.nativeBridge.postMessage(jsonString) : window.postBridgeMessage(jsonString) } },1000) }

實現思路

通過上面幾個問題的思考,我們對埋點的實現大致已經有了一些想法,我們使用思維導圖來還原下我們即將要做的事情,圖片記得放大看哦,太小了可能看不清。

  1. 我們需要暴露給業務方調用的方法

  1. 我們需要處理的事件類型

  1. SDK 的基本實現思路

我們來看下幾個核心代碼的實現

工具方法

我們定義了幾個工具方法,提高開發的幸福指數 ??

var helper = {}; // 生成一個唯一的標識,pageSessionId (用這個變數來關聯開始載入、載入完成、離開頁面的事件,計算出頁面加菜時間,停留時間) helper.uuid = function(){} // 元素綁定事件監聽,兼容瀏覽器到IE8 helper.on = function(){} //元素移除事件監聽的適配器函數,兼容瀏覽器到IE8 helper.remove = function(){} //將json轉為字元串,事件傳輸的參數類型轉化 helper.changeJSON2Query = function(){} //將相對路徑解析成文檔全路徑 helper.normalize = function(){}

採集邏輯

var collect = { deviceUrl:http://collect.trc.com/rest/collect/device/h5/v1, eventUrl:http://collect.trc.com/rest/collect/event/h5/v1, isuploadUrl:http://collect.trc.com/rest/collect/isupload/app/v1, parmas:{ ExtraInfo:{} }, device:{} }; //獲取埋點配置 collect.setParames = function(){} //更新訪問路徑及頁面信息 collect.updatePageInfo = function(){} //獲取事件參數 collect.getParames = function(){} //獲取設備信息 collect.getDevice = function(){} //事件採集 collect.send = function(){} //設備採集 collect.sendDevice = function(){} //判斷才否採集,埋點採集的開關 collect.isupload = function(){ 1. 判斷是否採集,不採集就註銷事件監聽(項目中區分遊客身份和用戶身份的採集情況,這個方法會被判斷兩次) 2. 採集則判斷是否已經採集過 a.已經採集過不做任何操作 b.沒有採集過添加事件監聽 3. 判斷是 混合應用還是純 web 應用 a.如果是web 應用,調用 collect.setIframe 設置 iframe b.如果是混合應用 將開始載入和載入完成事件傳輸給 app } //點擊事件處理函數 collect.clickHandler = function(){} //離開頁面的事件處理函數 collect.beforeUnloadHandler = function(){} //頁面回退事件處理函數 collect.onPopStateHandler = function(){} //系統事件初始化,註冊離開事件,瀏覽器後退事件 collect.event = function(){} //獲取記錄開始載入數據信息 collect.getBeforeload = function(){} //存儲載入完成,獲取設備類型,記錄載入完成信息 collect.onload = function(){ 1. 判斷cookie是否有存設備類型信息,有表示混合應用 2. 採集載入完成時間等信息 3. 調用 collect.isupload 判斷是否進行採集 } //web 應用,通過嵌入 iframe 進行跨域 cookie 通訊,設置設備id collect.setIframe = function(){} //app 與 h5 混合應用,直接將數信息發給 app,判斷設備類型做原生方法適配器 collect.saveEvent = function(){} //採集自定義事件類型 collect.dispatch = function(){} //將參數 userId 存入sessionStorage collect.storeUserId = function(){} //採集H5信息,如果是混合應用,將採集到的信息發送給 app 端 collect.saveEventInfo = function(){} //頁面初始化調用方法 collect.init = function(){ 1. 獲取開始載入的採集信息 2. 獲取 SDK 配置信息,設備信息 3. 改寫 history 兩個方法,單頁面應用頁面跳轉前調用我們自己的方法 4. 頁面載入完成,調用 collect.onload 方法 } collect.init(); // 初始化 //暴露給業務方調用的方法 return { dispatch:collect.dispatch, storeUserId:collect.storeUserId, }

擴展

??就是我這段時間研究的成果了,代碼的篇幅比較長,就不放在博客里了,感興趣的同學可以加我微信進行交流,或則在文章下面留言,也歡迎大家給我提意見,幫忙優化 ??。

web 瀏覽器指紋跨域共享

你需要知道的單頁面路由實現原理

數據埋點是什麼?設置埋點的意義是什麼?

數據採集與埋點

美團點評前端無痕埋點實踐

如何清楚易懂的解釋「UV和PV"的定義


推薦閱讀:

2016年8月汽車銷售數據分析:乘用車市場「龍虎鬥」
南寧市家事糾紛大數據分析報告·撫養編(2015-2016)
工業4.0:大數據驅動新型智能製造業
新:一組「價值萬金」的營銷數據

TAG:數據分析 | 數據 | 前端開發 |