Web用户行为跟踪收集

来源:互联网 发布:广安门中医院挂号软件 编辑:程序博客网 时间:2024/05/22 19:26

收集用户隐私的行径眼下已不再是什么新鲜的话题(与其说是收集,不如说是偷窥),就连G、MS也屡出风头,当然事出有因,企业通过无法八门的各种手段了解用户的行为,进而作为决策支持的依据;通常表现为跨领域的收集(浏览器、智能系统、OS etc.)、业务、产品数据的收集,当然收集的方式也不一而论。以下展示通过客户端脚本方式收集Web站点用户行为数据的实现方式,以此来判断页面加载过程、执行时间、用户停留时间、页面提交率、成功率、页面错误等(本示例实现较为简单,在特定复杂的业务数据收集时,可适当重构与改造)。

1、收集基础数据

基础数据涵盖

(1)  业务类:业务流页面地址、用户停留时间、打开次数、

会话ID、客户端IP、业务流步骤等

(2)  辅助类:浏览器、OS、COOKIE等

(3)  示例 

[javascript] view plaincopyprint?
  1. /* 
  2.     记录构造基础数据 
  3. */  
  4. var Logger = {  
  5.     Debug: false,  
  6.     UserControl: 'inputUser',  
  7.     HistoryControl: 'inputHistory',  
  8.     TimerControl: 'inputTimer',  
  9.     GetUser: function() {  
  10.         if (!$f(this.UserControl)) return ",,";  
  11.         return $f(this.UserControl).value;  
  12.     },  
  13.     /*-- attribute --*/  
  14.     Guid: function() {  
  15.         return this.GetUser().split(',')[0];  
  16.     },  
  17.     SessionID: function() {  
  18.         return this.GetUser().split(',')[1];  
  19.     },  
  20.     GetStep: function() {  
  21.         return this.GetUser().split(',')[2];  
  22.     },  
  23.     ProcessTime: function() {  
  24.         if (!$f(this.TimerControl)) return "0";  
  25.         return $f(this.TimerControl).value;  
  26.     },  
  27.     AppSys: 1, //不同系统编号  
  28.     Environment: ''//Environment.Dev:开发Dev、测试Test、正式Official  
  29.     IsNewSite: 1,  
  30.     //是否历史返回  
  31.     IsHistory: function() {  
  32.         if (!$f(this.HistoryControl)) return false;  
  33.         if ($f(this.HistoryControl).value.length > 0)  
  34.             return true;  
  35.         //if (this.IsSuccReturn()) return true; //成功返回 非历史  
  36.         return false;  
  37.     },  
  38.     IsStep2History: function() { //是否为第2步返回历史  
  39.         if (!$f(this.HistoryControl)) return false;  
  40.         var history = $f(this.HistoryControl).value;  
  41.         if (history.length == 0)  
  42.             return false;  
  43.         if (history.split(',').length > 1)  
  44.             return true;  
  45.         return false;  
  46.     },  
  47.     //是否为页面reload返回  
  48.     IsReturn: function() {  
  49.         if (typeof getUrlParam != "function"return false;  
  50.         var para = getUrlParam("return");  
  51.         if (para == "1"return true;  
  52.         return false;  
  53.     },  
  54.     //是否成功返回  
  55.     IsSuccReturn: function() {  
  56.         var para = getUrlParam("succret");  
  57.         if (para == "1"return true;  
  58.         return false;  
  59.     },  
  60.     //tracetype,guid,sessionid,processtime,description  
  61.     WriteStepLog: function() {  
  62.         var argc = arguments.length;  
  63.         var traceType = (argc > 0) ? arguments[0] : "";  
  64.         var guid = (argc > 1) ? arguments[1] : "";  
  65.         var sessionID = (argc > 2) ? arguments[2] : "";  
  66.         var processTime = (argc > 3) ? (arguments[3] == "" ? "0" : arguments[3]) : "0";  
  67.         var description = (argc > 4) ? arguments[4] : "";  
  68.         var url = (argc > 5) ? arguments[5] : "";  
  69.         /*with (Trace.Parameter) 
  70.         { 
  71.         TraceType = traceType; 
  72.         Guid = guid; 
  73.         SessionID = sessionID; 
  74.         PageUrl = window.location.href; 
  75.         ProcessTime = processTime; 
  76.         //set const value 
  77.         AppSys = 1; 
  78.         Environment = Environment.Dev; //Offical 
  79.         IsNewSite = 1; 
  80.         Description = description; 
  81.         }*/  
  82.         Trace.Parameter.TraceType = traceType;  
  83.         Trace.Parameter.Guid = guid;  
  84.         Trace.Parameter.SessionID = sessionID;  
  85.         if (url.length == 0) {  
  86.             url = window.location.href;  
  87.             //alert("self:" + window.location.href + ",refer:" + self.document.referrer);  
  88.             if (url.toLowerCase().indexOf('errorpage.aspx') > -1 && traceType.indexOf('ret') == -1) {  
  89.                 if (document.referrer != null && document.referrer != "") url = document.referrer;  
  90.             }  
  91.         }  
  92.         Trace.Parameter.PageUrl = url;  
  93.         Trace.Parameter.ProcessTime = processTime;  
  94.         Trace.Parameter.AppSys = this.AppSys;  
  95.         if (this.Environment.length == 0) this.Environment = Environment.Official;  
  96.         var curUrl = window.location.href.toLowerCase();  
  97.         if (curUrl.indexOf('https://') > -1) {  
  98.             this.Environment = this.Environment.replace('http://''https://');  
  99.         }  
  100.         Trace.Parameter.Environment = this.Environment;  
  101.         Trace.Parameter.IsNewSite = this.IsNewSite;  
  102.         Trace.Parameter.Description = escape(description);  
  103.         if (this.Debug) {  
  104.             alert(Trace.Parameter.TraceType + "," + Trace.Parameter.Guid + "," + Trace.Parameter.SessionID + ","  
  105.             + Trace.Parameter.ProcessTime + "," + Trace.Parameter.Description);  
  106.         }  
  107.         Trace.Submit(Trace.Parameter, null'img');  
  108.     },  
  109.     WriteOpenLog: function() {  
  110.         try {  
  111.             var argc = arguments.length;  
  112.             var step = (argc > 0) ? arguments[0] : "";  
  113.             var desc = (argc > 1) ? arguments[1] : "";  
  114.             if (typeof PTID != "undefined" && PTID.length > 0) {  
  115.                 desc += ",PTID:" + PTID;  
  116.             }  
  117.             var loginstep = this.GetStep();  
  118.             /*if (this.IsSuccReturn()) { //成功返回 
  119.             Logger.WriteStepLog(Step.succret, this.Guid(), this.SessionID(), this.ProcessTime(), desc); 
  120.             this.SetTimer(); 
  121.             }*/  
  122.             if (step == "step1" && !this.IsHistory() && typeof loginstep != "undefined" && loginstep.length > 0) { //登录返回(第一步发生)  
  123.                 Logger.WriteStepLog(loginstep, this.Guid(), this.SessionID(), this.ProcessTime(), desc);  
  124.             }  
  125.             else if (step == "step1" && !this.IsHistory() && !this.IsReturn()) //not history back,not page reload  
  126.             {  
  127.                 Logger.WriteStepLog(step, this.Guid(), this.SessionID(), this.ProcessTime(), desc);  
  128.             }  
  129.             else if ((step == "step2" && !this.IsStep2History()) || step == "step3") { //第2步、第3步  
  130.                 Logger.WriteStepLog(step, this.Guid(), this.SessionID(), this.ProcessTime(), desc);  
  131.                 this.SetTimer();  
  132.             }  
  133.             else if (step == "password" || step == "mobile" || step == "cancelbind") { //一点充没有历史返回等属性  
  134.                 Logger.WriteStepLog(step, this.Guid(), this.SessionID(), this.ProcessTime(), desc);  
  135.                 this.SetTimer();  
  136.             }  
  137.             else if (this.IsHistory() || this.IsStep2History() || this.IsReturn()) { //历史返回  
  138.                 Logger.WriteStepLog(step + "ret"this.Guid(), this.SessionID(), "0", desc);  
  139.                 this.SetTimer();  
  140.             }  
  141.             else {  
  142.                 Logger.WriteStepLog(step, this.Guid(), this.SessionID(), "0", desc);  
  143.             }  
  144.         }  
  145.         catch (e) {  
  146.   
  147.         }  
  148.     },  
  149.     WriteSubmitLog: function() {  
  150.         try {  
  151.             var argc = arguments.length;  
  152.             var step = (argc > 0) ? arguments[0] : "";  
  153.             var desc = (argc > 1) ? arguments[1] : "";  
  154.             var url = (argc > 2) ? arguments[2] : "";  
  155.             if (typeof PTID != "undefined" && PTID.length > 0) {  
  156.                 desc += ",PTID:" + PTID;  
  157.             }  
  158.             Logger.WriteStepLog(step, this.Guid(), this.SessionID(), this.ProcessTime(), desc, url);  
  159.             $f(this.HistoryControl).value = "1";  
  160.             //set step2  
  161.             if (step == "step2submit") {  
  162.                 $f(this.HistoryControl).value = "1,1";  
  163.             }  
  164.             this.SetTimer();  
  165.         }  
  166.         catch (e) {  
  167.         }  
  168.     },  
  169.     SetTimer: function() { //reset timer  
  170.         if (Timer && typeof Timer != "undefined") {  
  171.             Timer.Reset();  
  172.         }  
  173.     },  
  174.     DirectOpenLog: function() {  
  175.         try {  
  176.             var argc = arguments.length;  
  177.             var step = (argc > 0) ? arguments[0] : "";  
  178.             var desc = (argc > 1) ? arguments[1] : "";  
  179.             if (typeof PTID != "undefined" && PTID.length > 0) {  
  180.                 desc += ",PTID:" + PTID;  
  181.             }  
  182.             this.AppSys = 2;  
  183.             Logger.WriteStepLog(step, this.Guid(), this.SessionID(), this.ProcessTime(), desc);  
  184.             if (step != Step.step1) {  
  185.                 this.SetTimer();  
  186.             }  
  187.         }  
  188.         catch (e) {  
  189.         }  
  190.     }  
  191. };  
  192. var $f = function(name) {  
  193.     return document.getElementById(name);  
  194. }  
  195. //记录客户端脚本错误  
  196. window.onerror = function GetErrors(error) {  
  197.     try {  
  198.         var msg;  
  199.         for (var i = 0; i < arguments.length; i++) {  
  200.             if (i == 0 || i == 2) {  
  201.                 msg += " | " + arguments[i];  
  202.             }  
  203.         }  
  204.         if (msg.length > 0 && typeof Logger != 'undefined') {  
  205.             Logger.WriteStepLog('syserror''-''-''', msg);  
  206.         }  
  207.         window.onerror = null;  
  208.         return true;  
  209.     } catch (e) { };  
  210. }  

2、时间统计

通过在页面放置计时器,计算当前视图下用户的停留时间。

示例: 

[javascript] view plaincopyprint?
  1. /*--------Timer Script------------- 
  2.  
  3. * 页面计时器控件 
  4. * 1、Timer.BindControl = 'inputTimer'; 
  5. * 2、<input id="inputTimer" type="hidden" class="timer" /> 
  6. * 不写Cookie、不显示定时器时,采用(EndTime - StratTime)即可 
  7. */  
  8. var up, down;  
  9. var cmin1, csec1, clock;  
  10. var Timer = {  
  11.     Debug: false,  
  12.     BindControl: 'inputTimer',  
  13.     StartTime: '',  
  14.     EndTime: '',  
  15.     StartTimer: function() {  
  16.         if (!$f(this.BindControl)) return;  
  17.         if ($f(this.BindControl).value != ""return;  
  18.         //$("#" + this.BindControl).val("");  
  19.         cmin1 = 0;  
  20.         csec1 = 0;  
  21.         //每个页面单独记录,不需要采用Cookie,屏蔽  
  22.         //        var cookie = GetCookie("Timer");  
  23.         //        if (cookie) {  
  24.         //            cmin1 = parseInt(this.Minutes(cookie));  
  25.         //            csec1 = parseInt(this.Seconds(cookie));  
  26.         //            DeleteCookie("Timer");  
  27.         //        }  
  28.         //        else {  
  29.         //            cmin1 = csec1 = 0;  
  30.         //        }  
  31.         this.Repeat();  
  32.     },  
  33.     SetValue: function() {  
  34.         var html = $f(this.BindControl).value;  
  35.         if (html != null && html.length > 0) SetCookie("Timer", html);  
  36.     },  
  37.     Minutes: function(data) {  
  38.         for (var i = 0; i < data.length; i++) if (data.substring(i, i + 1) == ":"break;  
  39.         return (data.substring(0, i));  
  40.     },  
  41.     Seconds: function(data) {  
  42.         for (var i = 0; i < data.length; i++) if (data.substring(i, i + 1) == ":"break;  
  43.         return (data.substring(i + 1, data.length));  
  44.     },  
  45.     Display: function(min, sec) {  
  46.         var disp = "";  
  47.         if (min <= 9) disp += "0" + min + ":";  
  48.         else disp += min + ":";  
  49.         if (sec <= 9) disp += "0" + sec;  
  50.         else disp += sec;  
  51.         return (disp);  
  52.     },  
  53.     Repeat: function() {  
  54.         csec1++;  
  55.         if (csec1 == 60) { csec1 = 0; cmin1++; }  
  56.         $f(this.BindControl).value = this.Display(cmin1, csec1);  
  57.         if (this.Debug) $f("inputDebug").value = this.Display(cmin1, csec1);  
  58.         clock = window.setTimeout(function() { Timer.Repeat() }, 1000);  
  59.     },  
  60.     //重新开始计时  
  61.     Reset: function() {  
  62.         $f(this.BindControl).value = "";  
  63.         window.clearTimeout(clock);  
  64.         Timer.StartTimer();  
  65.     },  
  66.     AddTrigger: function() {  
  67.         var list = document.getElementsByTagName("INPUT");  
  68.         for (var i = 0; i < list.length; i++) {  
  69.             if (list[i].type.toUpperCase() == 'TEXT') {  
  70.                 if (list[i].addEventListener) {  
  71.                     list[i].addEventListener("keyup"function() { Timer.StartTimer(); }, false);  
  72.                 }  
  73.                 else {  
  74.                     list[i].attachEvent("onkeyup"function() { Timer.StartTimer(); });  
  75.                 }  
  76.             }  
  77.         }  
  78.     }  
  79. };  
  80. if (document.all) {  
  81.     window.attachEvent("onload"function() { Timer.AddTrigger() });  
  82. }  
  83. else {  
  84.     window.addEventListener("load"function() { Timer.AddTrigger() }, false);  
  85. }  
  86. if (Timer.Debug) {  
  87.     if (!document.getElementById("inputDebug")) {  
  88.         document.write("<input type='text' id='inputDebug' />");  
  89.     }  
  90. }  
  91. /* 兼容两种模式设定Cookie */  
  92. //$(window).unload(function() { Timer.SetValue(); });  
  93. //$("form").submit(function() { Timer.SetValue(); });  

3、异步记录

将收集到的数据通过GET、POST异步的方式发送到目标服务器

示例: 

[javascript] view plaincopyprint?
  1. /*--------Trace Script-------------*/  
  2. var Step =  
  3. {  
  4.     /* step */  
  5.     step1: "step1"//第一步打开,不含历史返回、成功返回  
  6.     step2: "step2",  
  7.     step3: "step3",  
  8.     /* post */  
  9.     step1submit: "step1submit"//第一步提交  
  10.     step2submit: "step2submit",  
  11.     step3submit: "step3submit",  
  12.     step3resubmit: "step3resubmit"//第三步重新提交  
  13.     /* success */  
  14.     success:"success"//操作成功  
  15.     succret: "succret"//成功返回  
  16.     step1ret: "step1ret"//返回第一步  
  17.     step2ret: "step2ret",  
  18.     step3ret: "step3ret",  
  19.     /* error */  
  20.     error:"error"//操作失败  
  21.     errstep1: "errstep1"//第一步出错 错误页面记录  
  22.     errstep2: "errstep2",  
  23.     errstep3: "errstep3",  
  24.     errstep1ret: "errstep1ret"//出错页返回到第一步  
  25.     errstep2ret: "errstep2ret",  
  26.     errstep3ret: "errstep3ret",  
  27.     /* login */  
  28.     loginb1: "loginb1"//银行卡登录返回  
  29.     loginc1: "loginc1"//实物卡登录返回  
  30.     loginbind: "loginbind"//一点充登录返回  
  31.     step1login: "step1login"//第一步登录界面  
  32.     /* other */  
  33.     bind: "bind"//用户绑定一点充  
  34.     mobile: "mobile"//修改手机号  
  35.     password: "password"//修改密码  
  36.     cancelbind: "cancelbind"//取消服务  
  37.     Login: "Login"//用户登录日志  
  38.     querydeposit: "querydeposit"//充值记录  
  39.     querycardbalance: "querycardbalance"//实物卡余额  
  40.     clickkf: "clickkf"//点击在线客服  
  41.     closekf: "closekf"//关闭在线客服  
  42.     clickaccount1: "clickaccount1"//点击新增常用账号  
  43.     clickaccount2: "clickaccount2" //点击新增确定按钮  
  44. };  
  45. var Environment = { Dev: "http://dev.xxx.com", Test: "http://test.xxx.com", Official: "http://www.xxx.com" }  
  46. var Trace = {  
  47.     AutoSubmit: false//是否在提交表单时自动处理  
  48.     Parameter: {  
  49.         TraceType: ''//TraceType.open  
  50.         Guid: '0',  
  51.         SessionID: '',  
  52.         PageUrl: '',  
  53.         Description: '',  
  54.         ProcessTime: '',  
  55.         IsNewSite: false,  
  56.         AppSys: 1,  
  57.         ClientIP: '',  
  58.         Environment: Environment.Official,  
  59.         Extend: {}  
  60.     },  
  61.     MyAjax: function() {  
  62.         this.xml = false;  
  63.         this.GetXmlHttp = function() {  
  64.             if (!this.xml && typeof XMLHttpRequest != 'undefined') {  
  65.                 this.xml = new XMLHttpRequest();  
  66.             }  
  67.             else {  
  68.                 var MSXML = ['MSXML2.XMLHTTP.5.0''MSXML2.XMLHTTP.4.0''MSXML2.XMLHTTP.3.0''MSXML2.XMLHTTP''Microsoft.XMLHTTP'];  
  69.                 for (var i = 0; i < MSXML.length; i++) {  
  70.                     try {  
  71.                         this.xml = new ActiveXObject(MSXML[i]);  
  72.                         break;  
  73.                     }  
  74.                     catch (e) {//alert(e.message);  
  75.                     }  
  76.                 }  
  77.             }  
  78.         }  
  79.         this.GetXmlHttp();  
  80.         var xmlHttp = this.xml;  
  81.         var ajax = this;  
  82.         var callBack = null;  
  83.         this.updatePage = function() {  
  84.             if (xmlHttp.readyState == 4) {  
  85.                 var response = xmlHttp.responseText;  
  86.                 if (callBack != null && typeof callBack == "function") {  
  87.                     callBack(response);  
  88.                 }  
  89.             }  
  90.         }  
  91.         this.toQueryString = function(json) {  
  92.             var query = "";  
  93.             if (json != null) {  
  94.                 for (var param in json) {  
  95.                     query += param + "=" + escape(json[param]) + "&"  
  96.                 }  
  97.             }  
  98.             return query;  
  99.         }  
  100.         //提交参数,回调函数, post、get方法  
  101.         this.invoke = function(params, pageCallBack, method) {  
  102.             if (xmlHttp) {  
  103.                 var query = "";  
  104.                 query += this.toQueryString(params);  
  105.                 query = query.substring(0, query.length - 1);  
  106.                 //var thisReg = new RegExp(/'|"/gi);  
  107.                 //query = query.replace(thisReg, "");  
  108.                 callBack = pageCallBack;  
  109.                 if (method != null && method.toUpperCase() == "GET") {  
  110.                     var url = params.Environment + "/Trace.aspx?" + query;  
  111.                     xmlHttp.onreadystatechange = ajax.updatePage;  
  112.                     xmlHttp.open("GET", url, true);  
  113.                     xmlHttp.setRequestHeader("TraceAjax-Ver""ver1.0");  
  114.                     xmlHttp.send(null);  
  115.                 }  
  116.                 else if (method != null && method.toUpperCase() == "POST") {  
  117.                     var url = params.Environment + "/Trace.aspx";  
  118.                     //  xmlHttp.setRequestHeader("Content-Length",query);   
  119.                     xmlHttp.onreadystatechange = ajax.updatePage; //new CallClient(this);  
  120.                     xmlHttp.open("POST", url, true);  
  121.                     xmlHttp.setRequestHeader("Content-type""application/x-www-form-urlencoded");  
  122.                     xmlHttp.setRequestHeader("TraceAjax-Ver""ver1.0");  
  123.                     xmlHttp.send(query);  
  124.                 }  
  125.                 else { //跨域  
  126.                     Trace.CreateTraceImg();  
  127.                     document.getElementById("traceImg").src = params.Environment + "/Trace.aspx?a=" + Math.random(1000000000) + "&" + query;  
  128.                 }  
  129.             }  
  130.         }  
  131.     },  
  132.     CreateTraceImg: function() {  
  133.         if (!document.getElementById("traceImg")) {  
  134.             document.write("<img id=\"traceImg\" style='display:none' />");  
  135.             //            var imgNode = document.createElement("img")  
  136.             //            imgNode.setAttribute("id", "traceImg")  
  137.             //            imgNode.style.display = "none";  
  138.             //            document.body.appendChild(imgNode);  
  139.         }  
  140.     },  
  141.     Submit: function(params, pageCallBack, method) {  
  142.         try {  
  143.             var ajax = new Trace.MyAjax();  
  144.             //校验step是否正确  
  145.             //        for (var i = 0; i < Step.length; i++) {  
  146.             //            if (params.TraceType == Step[i]) {  
  147.             //                alert(Step[i]);  
  148.             //            }  
  149.             //        }  
  150.             ajax.invoke(params, pageCallBack, method);  
  151.         }  
  152.         catch (e) {  
  153.         }  
  154.     }  
  155. };  
  156. Trace.CreateTraceImg();  

4、后端数据分析

结合前台收集的用户数据,后台初步可以完成,每一个页面的打开次数、占用时长,提交率、以及后台业务流的成功率等,通过报表形式展示数据分析界面,这样就可以监控用户行为异常、监控系统波动以及影响区域。

5、扩展

通过记录来源地址,可以更详尽的分析用户行为的流程。

通过追加页面元素的点击情况,从而计算点击率。

虽然服务器端也可以做到这些过程,但需求在不同的过程中插入众多的日志记录,进而POST到服务器端,过于繁冗!

另外跟踪服务器可对来源的数据请求验证其合法性,以及是否授权。

*注意:在Tracer的过程中,如遇采用系统方法如:绑定事件window.onload,需要注意不用覆盖了方法,避免与应用的页面冲突,可以通过来添加事件监听,eg:

[javascript] view plaincopyprint?
  1. AddTrigger: function() {  
  2.     var list = document.getElementsByTagName("INPUT");  
  3.     for (var i = 0; i < list.length; i++) {  
  4.         if (list[i].type.toUpperCase() == 'TEXT') {  
  5.             if (list[i].addEventListener) {  
  6.                 list[i].addEventListener("keyup"function() { Timer.StartTimer(); }, false);  
  7.             }  
  8.             else {  
  9.                 list[i].attachEvent("onkeyup"function() { Timer.StartTimer(); });  
  10.             }  
  11.         }  
  12.     }  
  13. }  

6、关于脚本压缩

推荐采用JSA压缩工具,原因:稳定、可靠、压缩质量有保证

不过需要大家安装一下JAVA RUNTIME



原文出处:http://blog.csdn.net/webwalker/article/details/2587172

0 0