WEB系统轨迹操作前端方案

由于工作需要,需要采集用户的操作轨迹,弹出框日志,界面加载时间等信息,经过网上资料查阅后,自己写了个js来采集用户行为,大家可以看下我的实现方式,有问题的可以给我留言,一起探讨和改正。

Web项目分析用户的操作行为主要有两种方式,一种较为宏观,主要是通过分析后台日志,分析用户调用后台的那些服务,各个页面之间的跳转关系,

还有一种较为微观,主要是记录用户对界面的操作行为,鼠标点击,键盘操作等分析用户。 通过在页面上的 JavaScript来记录,一线互联网的的界面操作轨迹就是通过js来实现 。

用户在网页上的行为是复杂的,但是大多数情况下,能直接取到的最有价值的信息是用户鼠标的行为。鼠标行为中,处理起来最为简单的是鼠标的点击。用户在界面上操作,点击并不是随机的,除了部分无意的点击外,大部分点击都是有意义的,表示用户对当前的区域有兴趣(哪怕是误解) 所以记录下用户的点击行为并进行分析是有意义的,通过分析用户的点击记录,可以找出页面上吸引用户的区域,这些区域不妨称之为热区。

实现方式,使用 JavaScript,监听鼠标的点击事件,触发时获取当前点击的鼠标位置,对应的html标签  等相关信息后 ,存储该信息于对应界面的js对象中数组中,待该界面关闭时把存储数据的数据通过“打点的形式”发送到指定的服务器,从而获取相关用户行为信息的过程。

150006_d2P6_263999.jpg


1 用户界面操作轨迹获取,存储与界面的初始化js对象数组中

2 界面请求系统服务器访问其他界面

3 界面发生跳转

4 当界面跳转前 调用window.onbeforeunload方法 

5 onbeforeunload 中 通过打点形式把数据发送到打点服务器

: 这里为什么不用ajax来实现数据发送,而是用打点的形式,原因如下:

打点:把要发送的信息加到 URL 参数中,请求打点服务器上的一个非常小的空图片,这样,信息就将记录在打点服务器的,之后再用专门的程序从日志中读取并分析相关信息。而且,对于打点服务器而言,由于只需要提供一个非常小的静态图片的访问,所以一台普通的服务器经过适当配置后也可以应对很高的访问量。

Ajax方式的话,界面需要等待消息的返回,这样会造成比较差的用户体验,通过打点不需要等待对方数据的返回

  

1.1  实现

   实现以js引入的形式,不需要改变业务系统所有的前端代码都在po.js中

 

1.1.1     取得用户在页面上的点击事件的位置坐标。

jquery获取鼠标点击事件采集数据

$(function(){
   /*鼠标下压采集用户行为*/
    $(document).mousedown(function(e){
        pv.mouseEvent(e);  
      });
    
});


1.1.2     打点方式

js生成img对象,调用对应的 img.src 形式发送数据,发送代码:

var _rometUlr="http://localhost:8089/ibop/images/po.gif";
var _img = new Image();
var _rnd_id = "_img_" + Math.random();
window[_rnd_id] = _img;
// 全局变量引用
_img.onload = _img.onerror = function(){
 window[_rnd_id]= null;// 删除全局变量引用
};
          
_img.src=_rometUlr+"?data=" + data + '&rnd=' + Math.random();


           

 

1.1.3     打点服务器数据获取

   打点服务器端数据获取使用java的过滤器,直接新建过滤器,监听po.gif请求

 

 

<!-- 用户行为采集过滤器 -->
  <filter>
    <filter-name>BehaviorCollectionFilter</filter-name>
   <filter-class>com.newland.sys.utils.BehaviorCollectionFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>BehaviorCollectionFilter</filter-name>
    <url-pattern>/images/po.gif</url-pattern>
  </filter-mapping>


Java代码:

  

 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      HttpServletRequest httpRequest = (HttpServletRequest) request;   
      String uri = httpRequest.getRequestURI();
      String strAllData = httpRequest.getQueryString();  //显示所有请求参数
      String strData = httpRequest.getParameter("data");
      //测试    
      if(log.isDebugEnabled()){
         log.debug("user behavior uri:>>"+uri);
          log.debug("user behavior data:>>"+strData);   
      }
      //取到数据该做啥做啥
      chain.doFilter(request, response);
 }


1.1.4     采集数据元素的封装

为满足一线互联网的数据采集的要求,数据对象分为3种,json格式

1.1.4.1          界面操作,统一归纳为鼠标操作,封装的数据报文格式为:

 

{
"id": _pageId,            //界面ID
"type": "mouse",          //mouse 鼠标操作
"tag": tname,             // HTML 标签
"tagid": targ.id,         // HTML 标签 ID
"value": targ.value,      //HTML标签值
"e.x": event.x,           // 坐标X
"e.y": event.y,           // 坐标Y
"timestamp": getTIme()    //时间戳
}


 

 

 

 

1.1.4.2            DOM加载,为界面初始化的行为,数据报文格式为:
{
 "id": _pageId,             //界面ID
 "type": "dom",             //DOM加载
 "start": _load_start,      //加载开始时间
 "end": _load_end,          //加载结束时间
 "load_time": _load_end - _load_start, //加载耗时
 "url": url,                //加载界面地址 
 "timestamp": getTIme()     //时间戳 
}


 

 

 

1.1.4.3           调用服务时的等待框行为,数据报文格式为:
{
 "id": _pageId,     //界面ID
 "type": "service", //服务加载
 "serviceName": serviceName, //服务名称
 "start": start,     //开始时间
 "end": end,         //结束时间
 "timestamp": getTIme()  //时间戳
}


实现的代码如下 po.js

/**
实例化用户行为对象
**/
var pv = (function(){
       var _load_start = new Date().getTime();
   var _pageName = document.location.href;    //文件路径
   var _pageId=null;   //页面流水
   var _rometUlr="http://localhost:8089/xxx/images/po.gif";
   var _THRESHOLD = 10;
    /**
 * 获取无格式化时间
 */
        var getTIme= function(){
                var now = new Date();
    var year = now.getFullYear();       //年
    var month = now.getMonth() + 1;     //月
    var day = now.getDate();            //日
    var hh = now.getHours();            //时
    var mm = now.getMinutes();          //分
    var ss = now.getSeconds();          //秒
    var clock = year + "";
    if(month < 10) clock += "0";
    clock += month + "";
    if(day < 10) clock += "0";
    clock += day + "";
    if(hh < 10)  clock += "0";
    clock += hh + "";
    if (mm < 10) clock += '0'; 
    clock += mm+ "";
    if(ss<10) clock += '0'; 
     clock += ss;
    return(clock); 
        }
         /**
 * 获取格式化时间
 */
   var getFormatTime=function()
{
var now = new Date();
    var year = now.getFullYear();       //年
    var month = now.getMonth() + 1;     //月
    var day = now.getDate();            //日
    var hh = now.getHours();            //时
    var mm = now.getMinutes();          //分
    var ss = now.getSeconds();
    var clock = year + "-";
    if(month < 10) clock += "0";
    clock += month + "-";
    if(day < 10) clock += "0";
    clock += day + " ";
    if(hh < 10)  clock += "0";
    clock += hh + ":";
    if (mm < 10) clock += '0'; 
    clock += mm+ ":";
    if(ss<10) clock += '0'; 
     clock += ss;
    return(clock); 
}
        /**
获取指定位数的随机数
**/
var mathRand = function(n) 
{ 
var _num="";
if(n==null)  n=1;
for(var i=0;i<n;i++) 
{ 
_num+=Math.floor(Math.random()*10); 
} 
return _num;
}
if(window.top==window.self){ //主界面
_pageId= "page"+getTIme()+mathRand(6);
//alert("Top页面展示获取id"+_pageId);
}
else if(window.top==window.parent){  //tab的iframe 界面
  _pageId= "page"+getTIme()+mathRand(6);
  //alert("TAB页面获取id"+_pageId);
}
    else if(window.parent!= window.top){  //tab界面中的子界面
       _pageId = window.parent.pv.pageId;
      // 页面在iframe中时处理
     //alert("使用parent的id="+_pageId);
}else if(window.opener!= window.top){  //tab界面中的子界面
 _pageId = window.parent.pv.pageId;
// alert("使用opener的id="+_pageId);
}else{
alert("没有取到pageId");
return;
}
      
//console.log("main:pageId"+_pageId);
var gatheringPoll =[];  //操作轨迹 
var _img = new Image();
var _rnd_id = "_img_" + Math.random();
window[_rnd_id] = _img; 
// 全局变量引用
_img.onload = _img.onerror = function(){
      window[_rnd_id]= null;// 删除全局变量引用
};
        var _s = function(){
          if(gatheringPoll.length>0){
      var data = JSON.stringify(gatheringPoll);
      _img.src=_rometUlr+"?data=" + encodeURI(data) + '&rnd=' + Math.random();
      
}
        };
        var _m = function(e){
                var targ ;
if (!e) var e = window.event ;
if (e.target) targ = e.target ;
else if (e.srcElement) targ = e.srcElement ;
if (targ.nodeType == 3) // defeat Safari bug 
   targ = targ.parentNode ;
var tname ;
tname=targ.tagName ;
            var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
            var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
            var x = e.pageX || e.clientX + scrollX;
            var y = e.pageY || e.clientY + scrollY;
//console.log("你点击了 " + tname + "元素"+"||value="+targ.value); 
var unit ={"id":_pageId,"type":"mouse","url":_pageName,"tag":tname,"tagid":targ.id, "value":targ.value,"e.x":x ,"e.y":y,"timestamp":getTIme()};
gatheringPoll.push(unit);
if(gatheringPoll.length>=_THRESHOLD){
  _s();
  gatheringPoll=[];
  } 
        };
        var _d=function(){
            if(document.readyState == "complete") //当页面加载状态
  {
   var _load_end = new Date().getTime(); 
   var url = document.location.href;    //文件路径
   var unit ={"id":_pageId,"type":"dom","url":_pageName,"start":_load_start,"end":_load_end,"load_time":_load_end-_load_start,"url":url,"timestamp":getTIme()};
   gatheringPoll.push(unit);
  } 
        };
        var  _t =function(msg){
           var unit ={"id":_pageId,"type":"tip","url":_pageName,"msg":msg,"timestamp":getTIme()};
           gatheringPoll.push(unit);
        };
        var  _sc=function(start,end,serviceName){
               var unit ={"id":_pageId,"type":"service","serviceName":serviceName,"url":_pageName,"start":start,"end":end,"timestamp":getTIme()};
gatheringPoll.push(unit);
     if(gatheringPoll.length>=_THRESHOLD){
  _s();
  gatheringPoll=[];
  }
        };
return {pageId : _pageId,pageName:_pageName,send : _s, mouseEvent:_m,domReady:_d,tipLog:_t, serviceLoding:_sc};
})(pv);
$(function(){
 /*鼠标下压采集用户行为*/
  $(document).mousedown(function(e){
   pv.mouseEvent(e);  
      });
  
});
/*页面关闭时发送数据*/
window.onbeforeunload =function(e){
pv.send();
}
//当页面加载完成时采集
document.onreadystatechange =function(e){pv.domReady();}


只要在页面中引用就可以了,侵入性很小,而且采集到数据时,用户体验不降低

<script src="js/po.js" type="text/javascript"></script>




转载于:https://my.oschina.net/5u3VuKiX1OPh1/blog/338146

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值