正如在宏觀介紹的博客中寫到的,做用戶行爲分析的方式有“前端埋點”和“後端埋點”的區分,真好今天敲了一個坤哥整理的“前端埋點”的程序,理解了之後結合demo來簡單講解“前端埋點”如何做。
前端埋點原理圖:
如上所示,從broswer到page,再到javascript以及後端backend,瀏覽器返回正常程序運行結果,本地文件中返回最終的log,這很像是在用戶程序中埋下了一段“暗代碼”,無形之中“竊取”了用戶的行爲信息,淘寶、網易等都有這樣的功能。
可以參考google做的Google Analysis這塊產品)
步驟:
- 埋點階段
- 數據收集階段
- 後端處理階段
如上,針對“前端埋點”,主要分爲這麼三部,對應到上面的原理圖,步驟一即(create script element),步驟二即collect client data,步驟三即backend和log.
下面,分別按照上述三步來展示代碼:
埋點階段
<script type="text/javascript">
var _maq = _maq || [];
_maq.push(['_setAccount', 'uuid']);
(function () {
var ma = document.createElement('script');
ma.type = 'text/javascript';
ma.async = true;
ma.src = "http://localhost:8091/data/js/ma.js";
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ma, s);
})();
</script>
這裏是正常的jsp、html頁面,在頁面的下端,往往插入一小段js代碼,即我們的“埋點”,如上所示,“埋點”中的全局數組,用於收集該頁面中需要被回傳的用戶行爲,比如:域名、ip、url、搜索的內容、常按的按鈕名稱……,(這裏暫時通過‘_setAccount’來傳遞了一個值爲‘vincent’的用戶名)
之後匿名的js函數,是埋點代碼的重中之重,如上代碼所示,在Dom節點添加名爲‘script’的元素,設置"ma.async = true",表示讓其異步執行,並將其src屬性指定爲一個單獨的js文件(將ma.js引入進來),最終將該element插到當前Dom樹上。而這個過程最終的目的即請求並執行上述的ma.js文件。
數據收集階段
(function () {
var params = {};
//Document對象數據
if (document) {
params.domain = document.domain || ''; //獲取域名
params.url = document.URL || ''; //當前Url地址
params.title = document.title || '';
params.referrer = document.referrer || ''; //上一跳路徑
}
//Window對象數據
if (window && window.screen) {
params.sh = window.screen.height || 0; //獲取顯示屏信息
params.sw = window.screen.width || 0;
params.cd = window.screen.colorDepth || 0;
}
//navigator對象數據
if (navigator) {
params.lang = navigator.language || ''; //獲取所用語言種類
}
//解析_maq配置
if (_maq) {
for (var i in _maq) { //獲取埋點階段,傳遞過來的用戶行爲
switch (_maq[i][0]) {
case '_setAccount':
params.account = _maq[i][1];
break;
default:
break;
}
}
}
//拼接參數串
var args = '';
for (var i in params) {
// alert(i);
if (args != '') {
args += '&';
}
args += i + '=' + params[i]; //將所有獲取到的信息進行拼接
}
//通過僞裝成Image對象,請求後端腳本
var img = new Image(1, 1);
var src = 'http://localhost:8091/data/dataCollection/log.gif?args=' + encodeURIComponent(args);
alert("請求到的後端腳本爲" + src);
img.src = src;
})();
如上代碼所示,是ma.js文件中的代碼,如上我做的註釋,可以將這個過程分爲3步驟;
- 解析、獲取用戶各種信息,如上:1.通過dom樹,獲取到的url,域名,上一跳信息;2.通過windows,獲取到的顯視屏的分辨率、長寬(前兩類通過內置的js對象獲取);3.通過_maq全局數組,獲取埋點時埋下的用戶行爲數據。
- 將上步的用戶信息按特定格式拼接,裝到args這個參數中。
- 僞裝成圖片,請求到後端controller中,並將args作爲http request參數傳入,做後端分析。
之所以使用圖片請求後端controller而不是ajax直接訪問,原因在於ajax不能跨域請求,ma.js和後端分析的代碼可能不在相同的域內,ajax做不到,而將image對象的src屬性指向後端腳本並攜帶參數,就輕鬆實現了跨域請求。
後端處理階段
@Controller
@RequestMapping("/dataCollection")
public class DataCollection {
@RequestMapping(value = "log.gif")
public void analysis(String args, HttpServletResponse response) throws IOException {
System.out.println(args);
//日誌收集
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/gif");
OutputStream out = response.getOutputStream();
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
ImageIO.write(image, "gif", out);
out.flush();
}
}
如上所示, 通過解析http request中的參數,即將在前端獲取到的用戶信息拿到了後端,這個爲了驗證,將其打印到控制檯,接下來就是做日誌收集工作了,到此前端獲取用戶信息已經完成。之後,生成一副1×1的空gif圖片作爲響應內容並將響應頭的Content-type設爲image/gif,返回到前端代碼中。
如圖,是在控制檯我拿到的用戶信息:
至此,前端埋點的實現以及原理就ok了,包括Google Analytics也是這麼個原理來做的。目前我跑的程序已經收集到了用戶的行爲信息,但是存在一個問題就是用戶刪除cookie信息,比如清楚瀏覽器緩存,會造成收集到的數據比實際訪問的行爲數據要多得多,這個問題接下來會繼續深入研究。
這個程序已經上傳到我的GitHub上,感興趣的朋友可以去下載,另外感謝網絡上的關於這塊的資料貢獻者,能讓我對行爲分析有深入的認識,多謝。
github地址:[email protected]:zhangzhenhua92/datacollection.git