JavaScript实现用户行为跟踪收集

来源:互联网 发布:淘宝网店加盟代理 编辑:程序博客网 时间:2024/06/08 00:35

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

1、收集基础数据

基础数据涵盖

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

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

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

(3)  示例 

/*     记录构造基础数据 */  var Logger = {      Debug: false,      UserControl: 'inputUser',      HistoryControl: 'inputHistory',      TimerControl: 'inputTimer',      GetUser: function() {          if (!$f(this.UserControl)) return ",,";          return $f(this.UserControl).value;      },      /*-- attribute --*/      Guid: function() {          return this.GetUser().split(',')[0];      },      SessionID: function() {          return this.GetUser().split(',')[1];      },      GetStep: function() {          return this.GetUser().split(',')[2];      },      ProcessTime: function() {          if (!$f(this.TimerControl)) return "0";          return $f(this.TimerControl).value;      },      AppSys: 1, //不同系统编号      Environment: '', //Environment.Dev:开发Dev、测试Test、正式Official      IsNewSite: 1,      //是否历史返回      IsHistory: function() {          if (!$f(this.HistoryControl)) return false;          if ($f(this.HistoryControl).value.length > 0)              return true;          //if (this.IsSuccReturn()) return true; //成功返回 非历史          return false;      },      IsStep2History: function() { //是否为第2步返回历史          if (!$f(this.HistoryControl)) return false;          var history = $f(this.HistoryControl).value;          if (history.length == 0)              return false;          if (history.split(',').length > 1)              return true;          return false;      },      //是否为页面reload返回      IsReturn: function() {          if (typeof getUrlParam != "function") return false;          var para = getUrlParam("return");          if (para == "1") return true;          return false;      },      //是否成功返回      IsSuccReturn: function() {          var para = getUrlParam("succret");          if (para == "1") return true;          return false;      },      //tracetype,guid,sessionid,processtime,description      WriteStepLog: function() {          var argc = arguments.length;          var traceType = (argc > 0) ? arguments[0] : "";          var guid = (argc > 1) ? arguments[1] : "";          var sessionID = (argc > 2) ? arguments[2] : "";          var processTime = (argc > 3) ? (arguments[3] == "" ? "0" : arguments[3]) : "0";          var description = (argc > 4) ? arguments[4] : "";          var url = (argc > 5) ? arguments[5] : "";          /*with (Trace.Parameter)         {         TraceType = traceType;         Guid = guid;         SessionID = sessionID;         PageUrl = window.location.href;         ProcessTime = processTime;         //set const value         AppSys = 1;         Environment = Environment.Dev; //Offical         IsNewSite = 1;         Description = description;         }*/          Trace.Parameter.TraceType = traceType;          Trace.Parameter.Guid = guid;          Trace.Parameter.SessionID = sessionID;          if (url.length == 0) {              url = window.location.href;              //alert("self:" + window.location.href + ",refer:" + self.document.referrer);              if (url.toLowerCase().indexOf('errorpage.aspx') > -1 && traceType.indexOf('ret') == -1) {                  if (document.referrer != null && document.referrer != "") url = document.referrer;              }          }          Trace.Parameter.PageUrl = url;          Trace.Parameter.ProcessTime = processTime;          Trace.Parameter.AppSys = this.AppSys;          if (this.Environment.length == 0) this.Environment = Environment.Official;          var curUrl = window.location.href.toLowerCase();          if (curUrl.indexOf('https://') > -1) {              this.Environment = this.Environment.replace('http://', 'https://');          }          Trace.Parameter.Environment = this.Environment;          Trace.Parameter.IsNewSite = this.IsNewSite;          Trace.Parameter.Description = escape(description);          if (this.Debug) {              alert(Trace.Parameter.TraceType + "," + Trace.Parameter.Guid + "," + Trace.Parameter.SessionID + ","              + Trace.Parameter.ProcessTime + "," + Trace.Parameter.Description);          }          Trace.Submit(Trace.Parameter, null, 'img');      },      WriteOpenLog: function() {          try {              var argc = arguments.length;              var step = (argc > 0) ? arguments[0] : "";              var desc = (argc > 1) ? arguments[1] : "";              if (typeof PTID != "undefined" && PTID.length > 0) {                  desc += ",PTID:" + PTID;              }              var loginstep = this.GetStep();              /*if (this.IsSuccReturn()) { //成功返回             Logger.WriteStepLog(Step.succret, this.Guid(), this.SessionID(), this.ProcessTime(), desc);             this.SetTimer();             }*/              if (step == "step1" && !this.IsHistory() && typeof loginstep != "undefined" && loginstep.length > 0) { //登录返回(第一步发生)                  Logger.WriteStepLog(loginstep, this.Guid(), this.SessionID(), this.ProcessTime(), desc);              }              else if (step == "step1" && !this.IsHistory() && !this.IsReturn()) //not history back,not page reload              {                  Logger.WriteStepLog(step, this.Guid(), this.SessionID(), this.ProcessTime(), desc);              }              else if ((step == "step2" && !this.IsStep2History()) || step == "step3") { //第2步、第3步                  Logger.WriteStepLog(step, this.Guid(), this.SessionID(), this.ProcessTime(), desc);                  this.SetTimer();              }              else if (step == "password" || step == "mobile" || step == "cancelbind") { //一点充没有历史返回等属性                  Logger.WriteStepLog(step, this.Guid(), this.SessionID(), this.ProcessTime(), desc);                  this.SetTimer();              }              else if (this.IsHistory() || this.IsStep2History() || this.IsReturn()) { //历史返回                  Logger.WriteStepLog(step + "ret", this.Guid(), this.SessionID(), "0", desc);                  this.SetTimer();              }              else {                  Logger.WriteStepLog(step, this.Guid(), this.SessionID(), "0", desc);              }          }          catch (e) {            }      },      WriteSubmitLog: function() {          try {              var argc = arguments.length;              var step = (argc > 0) ? arguments[0] : "";              var desc = (argc > 1) ? arguments[1] : "";              var url = (argc > 2) ? arguments[2] : "";              if (typeof PTID != "undefined" && PTID.length > 0) {                  desc += ",PTID:" + PTID;              }              Logger.WriteStepLog(step, this.Guid(), this.SessionID(), this.ProcessTime(), desc, url);              $f(this.HistoryControl).value = "1";              //set step2              if (step == "step2submit") {                  $f(this.HistoryControl).value = "1,1";              }              this.SetTimer();          }          catch (e) {          }      },      SetTimer: function() { //reset timer          if (Timer && typeof Timer != "undefined") {              Timer.Reset();          }      },      DirectOpenLog: function() {          try {              var argc = arguments.length;              var step = (argc > 0) ? arguments[0] : "";              var desc = (argc > 1) ? arguments[1] : "";              if (typeof PTID != "undefined" && PTID.length > 0) {                  desc += ",PTID:" + PTID;              }              this.AppSys = 2;              Logger.WriteStepLog(step, this.Guid(), this.SessionID(), this.ProcessTime(), desc);              if (step != Step.step1) {                  this.SetTimer();              }          }          catch (e) {          }      }  };  var $f = function(name) {      return document.getElementById(name);  }  //记录客户端脚本错误  window.onerror = function GetErrors(error) {      try {          var msg;          for (var i = 0; i < arguments.length; i++) {              if (i == 0 || i == 2) {                  msg += " | " + arguments[i];              }          }          if (msg.length > 0 && typeof Logger != 'undefined') {              Logger.WriteStepLog('syserror', '-', '-', '', msg);          }          window.onerror = null;          return true;      } catch (e) { };  }  


2、时间统计

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

示例: 

/*--------Timer Script------------- *  * 页面计时器控件 * 1、Timer.BindControl = 'inputTimer'; * 2、<input id="inputTimer" type="hidden" class="timer" /> * 不写Cookie、不显示定时器时,采用(EndTime - StratTime)即可 */  var up, down;  var cmin1, csec1, clock;  var Timer = {      Debug: false,      BindControl: 'inputTimer',      StartTime: '',      EndTime: '',      StartTimer: function() {          if (!$f(this.BindControl)) return;          if ($f(this.BindControl).value != "") return;          //$("#" + this.BindControl).val("");          cmin1 = 0;          csec1 = 0;          //每个页面单独记录,不需要采用Cookie,屏蔽          //        var cookie = GetCookie("Timer");          //        if (cookie) {          //            cmin1 = parseInt(this.Minutes(cookie));          //            csec1 = parseInt(this.Seconds(cookie));          //            DeleteCookie("Timer");          //        }          //        else {          //            cmin1 = csec1 = 0;          //        }          this.Repeat();      },      SetValue: function() {          var html = $f(this.BindControl).value;          if (html != null && html.length > 0) SetCookie("Timer", html);      },      Minutes: function(data) {          for (var i = 0; i < data.length; i++) if (data.substring(i, i + 1) == ":") break;          return (data.substring(0, i));      },      Seconds: function(data) {          for (var i = 0; i < data.length; i++) if (data.substring(i, i + 1) == ":") break;          return (data.substring(i + 1, data.length));      },      Display: function(min, sec) {          var disp = "";          if (min <= 9) disp += "0" + min + ":";          else disp += min + ":";          if (sec <= 9) disp += "0" + sec;          else disp += sec;          return (disp);      },      Repeat: function() {          csec1++;          if (csec1 == 60) { csec1 = 0; cmin1++; }          $f(this.BindControl).value = this.Display(cmin1, csec1);          if (this.Debug) $f("inputDebug").value = this.Display(cmin1, csec1);          clock = window.setTimeout(function() { Timer.Repeat() }, 1000);      },      //重新开始计时      Reset: function() {          $f(this.BindControl).value = "";          window.clearTimeout(clock);          Timer.StartTimer();      },      AddTrigger: function() {          var list = document.getElementsByTagName("INPUT");          for (var i = 0; i < list.length; i++) {              if (list[i].type.toUpperCase() == 'TEXT') {                  if (list[i].addEventListener) {                      list[i].addEventListener("keyup", function() { Timer.StartTimer(); }, false);                  }                  else {                      list[i].attachEvent("onkeyup", function() { Timer.StartTimer(); });                  }              }          }      }  };  if (document.all) {      window.attachEvent("onload", function() { Timer.AddTrigger() });  }  else {      window.addEventListener("load", function() { Timer.AddTrigger() }, false);  }  if (Timer.Debug) {      if (!document.getElementById("inputDebug")) {          document.write("<input type='text' id='inputDebug' />");      }  }  /* 兼容两种模式设定Cookie */  //$(window).unload(function() { Timer.SetValue(); });  //$("form").submit(function() { Timer.SetValue(); });  


3、异步记录

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

示例: 

/*--------Trace Script-------------*/  var Step =  {      /* step */      step1: "step1", //第一步打开,不含历史返回、成功返回      step2: "step2",      step3: "step3",      /* post */      step1submit: "step1submit", //第一步提交      step2submit: "step2submit",      step3submit: "step3submit",      step3resubmit: "step3resubmit", //第三步重新提交      /* success */      success:"success", //操作成功      succret: "succret", //成功返回      step1ret: "step1ret", //返回第一步      step2ret: "step2ret",      step3ret: "step3ret",      /* error */      error:"error", //操作失败      errstep1: "errstep1", //第一步出错 错误页面记录      errstep2: "errstep2",      errstep3: "errstep3",      errstep1ret: "errstep1ret", //出错页返回到第一步      errstep2ret: "errstep2ret",      errstep3ret: "errstep3ret",      /* login */      loginb1: "loginb1", //银行卡登录返回      loginc1: "loginc1", //实物卡登录返回      loginbind: "loginbind", //一点充登录返回      step1login: "step1login", //第一步登录界面      /* other */      bind: "bind", //用户绑定一点充      mobile: "mobile", //修改手机号      password: "password", //修改密码      cancelbind: "cancelbind", //取消服务      Login: "Login", //用户登录日志      querydeposit: "querydeposit", //充值记录      querycardbalance: "querycardbalance", //实物卡余额      clickkf: "clickkf", //点击在线客服      closekf: "closekf", //关闭在线客服      clickaccount1: "clickaccount1", //点击新增常用账号      clickaccount2: "clickaccount2" //点击新增确定按钮  };  var Environment = { Dev: "http://dev.xxx.com", Test: "http://test.xxx.com", Official: "http://www.xxx.com" }  var Trace = {      AutoSubmit: false, //是否在提交表单时自动处理      Parameter: {          TraceType: '', //TraceType.open          Guid: '0',          SessionID: '',          PageUrl: '',          Description: '',          ProcessTime: '',          IsNewSite: false,          AppSys: 1,          ClientIP: '',          Environment: Environment.Official,          Extend: {}      },      MyAjax: function() {          this.xml = false;          this.GetXmlHttp = function() {              if (!this.xml && typeof XMLHttpRequest != 'undefined') {                  this.xml = new XMLHttpRequest();              }              else {                  var MSXML = ['MSXML2.XMLHTTP.5.0', 'MSXML2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP'];                  for (var i = 0; i < MSXML.length; i++) {                      try {                          this.xml = new ActiveXObject(MSXML[i]);                          break;                      }                      catch (e) {//alert(e.message);                      }                  }              }          }          this.GetXmlHttp();          var xmlHttp = this.xml;          var ajax = this;          var callBack = null;          this.updatePage = function() {              if (xmlHttp.readyState == 4) {                  var response = xmlHttp.responseText;                  if (callBack != null && typeof callBack == "function") {                      callBack(response);                  }              }          }          this.toQueryString = function(json) {              var query = "";              if (json != null) {                  for (var param in json) {                      query += param + "=" + escape(json[param]) + "&"                  }              }              return query;          }          //提交参数,回调函数, post、get方法          this.invoke = function(params, pageCallBack, method) {              if (xmlHttp) {                  var query = "";                  query += this.toQueryString(params);                  query = query.substring(0, query.length - 1);                  //var thisReg = new RegExp(/'|"/gi);                  //query = query.replace(thisReg, "");                  callBack = pageCallBack;                  if (method != null && method.toUpperCase() == "GET") {                      var url = params.Environment + "/Trace.aspx?" + query;                      xmlHttp.onreadystatechange = ajax.updatePage;                      xmlHttp.open("GET", url, true);                      xmlHttp.setRequestHeader("TraceAjax-Ver", "ver1.0");                      xmlHttp.send(null);                  }                  else if (method != null && method.toUpperCase() == "POST") {                      var url = params.Environment + "/Trace.aspx";                      //  xmlHttp.setRequestHeader("Content-Length",query);                       xmlHttp.onreadystatechange = ajax.updatePage; //new CallClient(this);                      xmlHttp.open("POST", url, true);                      xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");                      xmlHttp.setRequestHeader("TraceAjax-Ver", "ver1.0");                      xmlHttp.send(query);                  }                  else { //跨域                      Trace.CreateTraceImg();                      document.getElementById("traceImg").src = params.Environment + "/Trace.aspx?a=" + Math.random(1000000000) + "&" + query;                  }              }          }      },      CreateTraceImg: function() {          if (!document.getElementById("traceImg")) {              document.write("<img id=\"traceImg\" style='display:none' />");              //            var imgNode = document.createElement("img")              //            imgNode.setAttribute("id", "traceImg")              //            imgNode.style.display = "none";              //            document.body.appendChild(imgNode);          }      },      Submit: function(params, pageCallBack, method) {          try {              var ajax = new Trace.MyAjax();              //校验step是否正确              //        for (var i = 0; i < Step.length; i++) {              //            if (params.TraceType == Step[i]) {              //                alert(Step[i]);              //            }              //        }              ajax.invoke(params, pageCallBack, method);          }          catch (e) {          }      }  };  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


0 0
原创粉丝点击