前端三种埋点

埋点到底是什么呢?

引用自百科的原话是,埋点分析网站分析的一种常用的数据采集方法。因此其本质是分析,但是靠什么分析呢?

靠埋点得到的数据。通俗来讲,就是当我想要在某个产品上得到用户的一些行为数据用来分析,就可以用埋点了。举个栗子,A用户把某本书加到了自己的书架了,我可以通过该用户书架的书的类型,由此分析该用户的阅读偏好,更深一步,通过对用户偏好的判断,我可以自动像用户推荐同类型的书,或者可以根据用户加入书架的时间,判断用户的碎片时间,在此时间段,可以定点向用户推送一些消息等。

我们可以看出,充分的埋点数据,有助于准确的分析用户的行为,为产品的调整提供方向。

怎么埋点呢?

要想知道埋点的方法,首先要了解埋点的分类,目前埋点主要分为三大类,分别是:

1.代码埋点

2.无埋点

3.可视化埋点(可认为是无埋点的一种)

已经知道了埋点的分类了,那么具体怎么实施呢,因其依靠数据,因此其步骤有三:

1.获取数据

2.展示数据

3.分析数据

充分准确的埋点是第一步,对后续的展示及分析都有重要的意义,因此本文重点介绍该方面。

埋点类别详解

1. 代码埋点

优点:监控用户行为,监测数据准确

缺点:工作量大,需要手动在需要埋点的地方进行埋点,因此需要侵入业务代码,比如点击事件的回调函数、页面的生命周期、ajax回调等。

常用代码埋点类型分两类,分别为命令式、声明式,可查看如下举例。

//命令式
$('button').click(()=>{
    record(data);
});//声明式
<button data-record = '{key:"recordTest",data:"recordData"}'>记录</button>
复制代码

命令式埋点:在一些事件操作的回调函数中进行埋点,埋点的数据和方法可能多种多样的,比如图片上带数据,ajax发送数据等。

声明式埋点:将埋点信息封装在自定义属性中,通过sdk识别自定义属性然后获取埋点数据。

2. 无埋点

优点:不需要关注埋点逻辑

缺点:给数据传输增加压力、无法定制

无埋点统计数据基本流程

 

通常,当页面打开时,页面中的埋点js片段会被执行,这段js代码会异步加载一个js文件,该文件就是无埋点的sdk,会被浏览器请求到并执行,通过该脚本进行数据收集,当数据收集完成后,可以利用一些方法将数据传递给后端进行收集整理。

无埋点sdk执行阶段

<script type="text/javascript">var _bury = _bury||[];
_bury.push(["_testData","网站标识"]);
(function(){
    var jsnode = document.createElement('script');
    jsnode.type='text/javascript';
    jsnode.async=true;
     //这里填入js sdk链接
    jsnode.src= 'xxxxxxx/bury_test.js';
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(jsnode,s);
})</script>

通过在页面或者基础脚本中集成这段代码,可以在对应的页面上引入我们的bury_test脚本,而bury_test脚本就是我们的埋点sdk。

埋点sdk

(function() {
  var buryData = {};
  //常用信息
  if (document) {
    //域名
    buryData.domain = document.domain || '';
    //标题
    buryData.title = document.title || '';
    //访问来源
    buryData.referrer = document.referrer || '';
    //分辨率
    buryData.sw = window.screen.width || 0;
    buryData.sh = window.screen.height || 0;
    //设备信息
    buryData.lang = navigator.language || '';
    buryData.ua = navigator.userAgent || '';
    //页面加载时间
    buryData.loadT = window.performance.timing.domContentLoadedEventEnd - window.performance.timing.navigationStart || 0;
  }
  //整理埋点数据
  var arg = [];
  if (buryData) {
    for (var i in buryData) {
      arg.push(encodeURIComponent(i) + '=' + encodeURIComponent(buryData[i]));
    }
  }
  var args = arg.join('&');
})

通过以上方法,可以获取一些基本的页面数据,更多详细的数据,可以根据具体的业务需求进行添加。

如何将采集到的数据进行上报呢,需要根据具体的情况来分析了,如果没有跨域的话,最简单的当然是ajax了。但是很多sdk都涉及到跨域了,目前主流的一种方法是用js脚本创建Image对象,将image的src指向后端脚本,并将数据拼接上。

3. 可视化埋点

优点:通过集成sdk,运营可自主选择,操作便捷。

缺点:无法定制详细的业务数据,比如 金额、商品数量等,该类数据需要实时变化;

需要统一规范,无法用在不同的设备上,比如某些特殊的设备imei并不能识别。

可视化埋点与代码埋点的对比

 

 

目前很多商用软件比如Mixpanel、TalkingData、诸葛IO、腾讯MTA等都可以用来可视化埋点,用户仅需要点击想要监测的元素,然后对该埋点起个对应的名字,并给个编号,就进行了埋点。

可视化埋点的核心方案是利用xpath,是在xml文档中查找信息的语言,如下图所示

 

 

通过上图方法,得到的xpath为//*[@id="1"]/div/div[2]/p[1] 如果将其换做dom的选择器,则为:#1>div>div:nth-of-type(2)>p:nth-of-type(1),由此,可以定位到固定的DOM节点。

如何获取xpath呢,这里可以提供一种方法可供参考:

var getPath = function(elem) {
  if (elem.id != '') {
    return '//*[@id=\"' + elem.id + '\"]';
  }
  if (elem == document.body) {
    return '/html/' + elem.tagName.toLowerCase();
  }
  var index = 1,
    siblings = elem.parentNode.childNodes;
  for (var i = 0, len = siblings.length; i < len; i++) {
    var sibling = siblings[i];
    if (sibling == elem) {
      return arguments.callee(elem.parentNode) + '/' + elem.tagName.toLowerCase() + '[' + (index) + ']';
    } else if (sibling.nodeType == 1 && sibling.tagName == elem.tagName) {
      index++;
    }
  }
}

通过上述方法,当我们点击某个元素时,将触发的元素event.target传入,即可得到完整的xpath。

三种埋点的区别

以百度举例: 当用户点击百度一下的时候,无埋点和可视化埋点可以获取的信息有某个时刻、某个设备进行了一次搜索,甚至可以获得部分搜索信息等,但是用户在输入搜索信息时,是否进行了修改、反复删除重新输入几次等深度的业务信息,无埋点和可视化埋点是统计不到的,则需要代码埋点。

App可视化埋点方案

背景

目前统计已经是一个产品常见的需求,尤其在业务模式探索的前期,埋点功能更是必不可少的功能,下面将介绍最简单的app全埋点方案!

什么是数据埋点

数据埋点是一般项目采用统计UV,PV,Action,Time等一系列的数据信息,对特定用户行为或事件进行捕获、处理和发送的相关技术及其实施过程。

为什么要数据埋点

产品或运营分析人员,基于埋点数据分析需要,对用户行为的每一个事件进行埋点布置,并通过SDK上报埋点的数据结果,进行分析,并进一步优化产品或指导运营。

数据埋点包括哪些

这里有我之前写的一篇文章App优质精准的用户行为统计和日志打捞方案

地址:http://blog.csdn.net/sk719887916/article/details/50931485

数据埋点采集模式

自动埋点

App通过代理,调用Sdk相关API,进行的将数据埋点上报的模式.

无痕埋点

无需通过专门提供代理类,直接由sdk提供相关接口,或者通过编译工具,预编译替换代码等,直接由sdk全部负责采集上报

可视化埋点

可视化埋点指 前端或者app端基于dom 元素和控件所精准自动埋点的上报的方案。

对比分析:

自动埋点:

缺点:
1 开发人员工作量大,需对业务提供唯一的ID,来区分每一个业务,无论是否提供sdk代理,业务开发人员至少需要多次调用sdk相关API.

2 业务人员和产品沟通成本提高,需要对具体业务制定相关的业务标识,以便于产品分析和统计

优点:

产品运营工作量少,对照业务映射表,就能分析出还原相关业务场景, 数据比较精细,无需大量的加工和处理。

无痕埋点

缺点:

1 sdk开发人员需提供一套无痕埋点技术成品,包括能正确获取PV,UV,ACtion,TIme等多项统计指标。前期技术投入大。

2 数据量大,需后端落地进行大量处理,并由产品进行自我还原业务员场景。 无论采用智能系统平台,还是通过原生的数据库查询数据,都是一种大量的分析精力。

优点:

1 开发人员工作量小,无需对业务标识进行唯一区分,由sdk自动进行生成,ID规则由sdk和产品进行约定。减少业务人员的沟通成本和使用步骤。

2 数据量全面,覆盖面广,产品可按需进行分析。做到毫无遗漏。

3 支持动态页面和局部动效的统计。

可视化埋点

优点:

1 相对数据量而言
相比较于无埋点相而言对较低,但是这个可视化元素的识别技术是客户端或者前端所要实现的,唯一id生成也无需客户端去自定义规则,这套生成规则由相关产品在自动化工具的情况下生成配置表,下发到客户端,再由客户端按坑就班到相关界面去实现。

2 数据量相对精确

缺点:

1 可视化工具的平台的搭建,静态页面的元素识别都需要额外开发。
2 动态效果可能会遗漏。

实现方案:

埋点需求可参考我之前的文章:

App优质精准的用户行为统计和日志打捞方案

App打造自定义的统计SDK

自动埋点实际上也是,提供一个base类,由业务类继承base类,在base里面做相关统计api调用,
可参考我的github:https://github.com/Tamicer/SkyMonitoring

核心实现:

以android作为列子:

提供自动遍历元素 并能扑捉点击的控件的activity, 并能在生命周期统计pv的打开和关闭,调用我开源的SkyMonitoring的对应的api.

复写dispatchTouchEvent(MotionEvent ev) 事件函数,确定被点击的view的相关位置,并生成唯一的ID,企业级app都是从服务器下发对应的ID,对应页面去调用埋点sdk Api,实现事件行为TcStatInterface.initEvent(path.viewTree);。

这个path就是view的路径,页面的深度路径,包括打开和关闭sdk在SkyMonitoring中已能自动获取。

本次demo是id生成规则是按照 :包名+ Activity+ Viewgroup+ Layout+ view + View index + viewID实现的。

业务直接去继承TamicActivity即可,就能去实现所有可视化view的埋点功能。

代码如下:

 “`
public abstract class TamicActivity extends AppCompatActivity {

private int statusBarHeight;
View rootView;
String rootViewTree;
String bigDataPrefix;
String bigDataIngorePrefix;
String bigDataEventPrefix;
private String TAG  = "LYK";

@Override
public void onAttachedToWindow() {
    super.onAttachedToWindow();
    //获取到根节点的view
    rootView = getWindow().getDecorView();
    //控件在视图树上的根路径
    rootViewTree = getPackageName() + "." + getClass().getSimpleName();
    //前缀名 bigData
    bigDataPrefix = "Tamic_test";
    //前缀名 bigData_
    bigDataIngorePrefix = bigDataPrefix + "";
    //前缀名 bigdata_ignore
    bigDataEventPrefix =  bigDataIngorePrefix +"Igmore";
}

@Override
protected void onResume() {
    super.onResume();

    TcStatInterface.recordPageStart(TamicActivity.this);
}

@Override
protected void onPause() {
    super.onPause();

    TcStatInterface.recordPageEnd();
}

@Override
protected void onDestroy() {
    super.onDestroy();
    // APP退出
    TcStatInterface.recordAppEnd();

}


@Override
public boolean dispatchTouchEvent(MotionEvent ev) {

    if(ev.getAction() == MotionEvent.ACTION_DOWN){
        ViewPath path = findClickView(ev);
        if(path != null) {
            Log.e(TAG, "path -->" + path.viewTree);
            TcStatInterface.initEvent(path.viewTree);
        }
    }
    return super.dispatchTouchEvent(ev);
}

private ViewPath findClickView(MotionEvent ev) {
    Log.e(TAG, "bigdata-->findClickView");
    ViewPath clickView = new ViewPath(rootView, rootViewTree);
    return searchClickView(clickView, ev, 0);
}


private ViewPath searchClickView(ViewPath myView, MotionEvent event, int index) {
    ViewPath clickView = null;
    View view = myView.view;
    if (isInView(view, event)) {   
        myView.level++;
        if (myView.level == 2 && !"LinearLayout".equals(view.getClass().getSimpleName())) {
            myView.filterLevelCount++;
        }
        if (myView.level > myView.filterLevelCount) {
            myView.viewTree = myView.viewTree + "." + view.getClass().getSimpleName() + "[" + index + "]";
        }
        Log.i(TAG, "bigdata-->tag = " + view.getTag());
        if (view.getTag() != null) {
            // 主动标记不需要统计时,不进行自动统计
            String tag = view.getTag().toString();
            if (tag.startsWith(bigDataIngorePrefix)) {
                return null;
            } else if (tag.startsWith(bigDataPrefix)) {
                if (tag.startsWith(bigDataEventPrefix)) {
                    myView.specifyTag = tag.replace(bigDataEventPrefix, "");
                }
                return myView;
            }
        }
        if (view instanceof ViewGroup) {   
            if (view instanceof AbsListView) {
                Log.i(TAG, "bigdata-->AbsListView ");
                return null;
            }
            ViewGroup group = (ViewGroup) view;
            int childCount = group.getChildCount();
            if (childCount == 0) {
                return myView;
            }
            for (int i = childCount - 1; i >= 0; i--) {
                myView.view = group.getChildAt(i);
                clickView = searchClickView(myView, event, i);
                if (clickView != null) {
                    return clickView;
                }
            }
        } else {
            clickView = myView;
        }
    }
    return clickView;
}

private boolean isInView(View view, MotionEvent event) {
    if (view == null || view.getVisibility() != View.VISIBLE) {
        return false;
    }
    int clickX = (int) event.getRawX();
    int clickY = (int) event.getRawY();
    int[] location = new int[2];
    view.getLocationOnScreen(location);
    int x = location[0];
    int y = location[1];
    int width = view.getWidth();
    int height = view.getHeight();
    return clickX > x && clickX < (x + width) && clickY > y && clickY < (y + height);
}

}

App项目集成使用,初始化url和相关统计配置字典,这个字典可以从服务器下发下来,我本次只是通过简单的本地文件做实践。

public class StatAppliation extends Application {

@Override
public void onCreate() {
    super.onCreate();
    // you app id
    int appId = 21212;
    // assets
    String fileName = "my_statconfig.json";
    String url = "https://github.com/Tamicer/TamicAppMonitoring";
    // init statSdk
    TcStatInterface.initialize(this, appId, "you app chanel", fileName);
    TcStatInterface.setUrl(url);
    TcStatInterface.setUploadPolicy(TcStatInterface.UploadPolicy.UPLOAD_POLICY_DEVELOPMENT, TcStatInterface.UPLOAD_TIME_ONE);
 }
}

可视化也可以通过aop插桩实现,但是实现起来对代码的入侵性太高,这里不做介绍。

aop插桩对碎片化fragment支持比较好。对这块的介绍可看我以前在公众号推送的一篇文章
AOP编程之AspectJ实战实现数据无痕埋点

可参考:
https://www.baidu.com/link?url=FniQOFyj1pd6O5Fz6viRMN3ZgexIKAk7SQ08EgpBU9cHHMszPlm2jRXJ21mkomtY&wd=&eqid=ffc87acf0005fd18000000045a5d98dd

可参考项目地址:

github:https://github.com/Tamicer/TamicAppMonitoring

posted @ 2021-08-04 17:50  可爱的小猫咪  阅读(2118)  评论(0编辑  收藏  举报