用户行为分析模型实践(三)——H5通用分析模型

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

作者vivo 互联网大数据团队- Zhao Wei、Tian Fengbiao、Li Xiong

本文从提升用户行为分析效率角度出发详细介绍了H5埋点方案规划埋点数据采集流程提供可借鉴的用户行为数据采集方案且完整呈现了针对页面分析留存分析的数仓模型规划方案在数仓模型设计过程中遇见的痛点难点问题也相应的给出了解决思路及案例代码在数据展示模块提供了分析指标数据展示的逻辑流程及UI案例旨在帮助有需要的同学全方位的了解用户行为数据全链路分析流程。

一、背景

针对用户行为数据进行采集有个专业术语叫埋点在h5页面上做的埋点统称为H5埋点。H5页面因其灵活性便捷的交互和丰富的功能以及在移动设备上支持多媒体等特点目前被广泛应用于网页app开发。

现阶段H5埋点的自由度较高行业数据产品在同类高频的业务场景上设计的时间花费较多埋点开发、埋点测试等事项耗时且需重复劳动同样的埋点数据分析层面-基础分析指标留存指标页面分析等需求需多次开发模型浪费宝贵的人力资源。

H5通用分析模型旨在通过规范化埋点设计方案开发设计一套通用度高扩展方便需求响应迅速的模型减少行业数据产品和开发在类似需求上的人力投入提升数据分析效率。

二、分析模型概述

2.1 术语解释

2.2 模型概述

针对业务发展的不同阶段会有相应的数据分析需求。如图1在业务初期用户的访问留存情况等是阶段性分析重点业务产品运营可以根据分析数据适时的调整页面布局运营策略等应用发展中后期可能会更多的关注订单、转化、路径等相关分析指标。如果能在应用上线之初快速的拿到核心分析指标数据对产品的推广迭代无疑是收益良多。所以本次模型构建从应用初期分析最广泛的核心指标出发落地应用概况、页面访问、用户留存等维度全方位核心分析指标体系。

图1应用生命周期内指标分析情况

2.2.1 分析模型主题

本次通用分析模型围绕以下分析主题构建。

  • 【基础分析】从用户浏览次数人均访问页面数人均使用时长新老用户等基础指标展示用户访问大盘数据。

  • 【页面分析】面向具体页面分析用户访问pv,uv,访问时长等核心指标有针对性的发现页面访量薄弱环节为合理化页面管理提供数据支撑协助产品经理通过信息重组提升页面访问量。

  • 【留存分析】通过用户的留存了解目前的产品现状用户的哪些行为导致留存率的不同; 判断产品的改进有无效果用户行为是否发生了改变导致留存率的提升留存分析反映了用户由初期的不稳定用户转化为活跃用户稳定用户忠诚用户的过程。

2.2.2 分析指标定义

以下示例中数据均为参考数据非真实数据

1、基础分析访问pvuv等指标全维度

2、页面分析页面访问相关pvuv时长等指标

注用户对访问页面进行命名分析平台提供配置入口方便用户对页面进行命名。

3、留存分析新用户留存活跃用户留存  包括N日内留存 和 第N日留存。

通常意义上的留存分析指的是用户在APP产生行为后在固定的第N日继续访问或使用APP的用户包括活跃用户留存和新用户留存

为满足不同业务的分析需求。此次留存模型包含 n日内留存分析即用户在APP产生行为后在固定的第N日内继续访问或使用APP的用户日期范围留存。

三、埋点方案

3.1 业务目标

  • 采集用户的pvuv数据帮助产品同学了解目前的产品现状并不断改进产品

  • 自动采集对pvuv等这类埋点业务无需再开发打开开关即可采集这类数据。

3.2 自动采集

3.2.1 什么是自动采集

自动采集是相对于前端开发者而言目的是为了帮助前端开发者提升数据采集效率。通过自动采集开关配置无需在手动实现上报逻辑。使用时前端开发者通过引入h5sdk.js(也称jssdk.js)打开自动采集开关我们就会在适当的时机以适当的规则采集数据并进行上报。开发者无需在关注采集代码内部逻辑以此来减轻同类数据采集的开发工作量。

3.2.2 如何自动采集

按照给定的规则进行页面事件EventListener当用户活动触发对应的事件时我们会组装好数据然后将组装好的数据通过https传入到后台。

3.2.3 自动采集的三大规则场景

我们的网站是一个SPA应用。SPA应用通过改变前端路由的变化实现页面内组件的切换。组件的切换对于一个非前端开发者来说可以泛指页面的切换。所以我们第一场景是要覆盖url变化的这类事件。在实践中我们发现当我们需要采集页面的用户停留时长时往往会不准确。为什么不准确用户可以缩小化浏览器也可以切换tab到其他网站这个时候计算的用户时长是不准确的。因为用户虽然打开了我们网页但是并没有聚焦到我们的网页。这种不应该算作用户停留时长因此对于这些行为我们又加上了失去焦点得到焦点以及切换浏览器tab事件的EventListener这两种场景。

综上三大场景总结如下

  1. 页面切换时进行采集即url变化时触发的事件

  2. 页面失去焦点得到焦点时进行采集。即focusblur事件

  3. 页面通过浏览器tab切换离开切换回来时进行采集即visibilitychange事件

3.2.3.1 三大规则场景的界定

上文我们已经在实践中总结出了自动采集的三大场景在实际应用针对三大场景的使用我们也总结出了一套界定方案。

1规则一界定——怎么判断页面切换

a、现在的网站要么是MPA要么是SPA模式或者两种模式混合MPA主要是后台路由SPA主要是前端路由hash模式和history模式。但无论是SPA还是MPA当页面需要切换时url一定会变化基于此点我们判断当url变化时用户一定切换了页面。此时触发规则一的事件产生数据上报。

这里需要注意2个问题

  • 第1个问题url变化 = window.location.origin + window.location.pathname + window.location.hash 这三部分的任一部分变化即为url变化并不包括window.location.search这部分的变化

  • 第2个问题在SPA中如果一个页面内有多个tab当切换tab时开发者也改变他的url的window.location.pathname此时也会认为是页面切换也会产生上报数据如下这种情况。

图2

b、完整页面切换上报流程由页面A切换到页面B时一共上报4个埋点

图3

c、关于路由的EventListener

现在的大多网站大多是SPA应用SPA的前端路由有hash模式和history模式这两种模式当通过前端路由来页面切换时肯定会触发hash模式或history相关的api。

因此我们只需要把所有触发事件的场景给全部进行EventListener即可。有如下2种路由的EventListenerwindow.hashchange事件——触发hash模式时、window.popstate事件、pushstate,replacestate自定义事件——触发history模式时。

这里有2个问题需要关注一是当某个SPA应用的路由事件触发了history模式时我们应该移除hash模式的EventListener。二是pushstate,replacestate自定义事件因为BOM并没有提供相关的api支持EventListener需要自行封装使用如下code。

引入JSSDK

/**
 * 拼接通用化上报参数
 * @param {string} 重写路由事件类型
 */
function resetHistoryFun(type){
    // 将原先的方法复制出来
    let originMethod = window.history[type]
    // 当window.history[type]函数被执行时这个return出来的函数就会被执行
    return function(){
        // 执行原先的方法
        let rs = originMethod.apply(this, arguments)
        // 然后自定义事件
        let e = new Event(type.toLocaleLowerCase())
        // 将原先函数的参数绑定到自定义的事件上去原先的是没有的
        e.arguments = arguments
        // 然后用window.dispatchEvent()主动触发
        window.dispatchEvent(e)
        return rs;
    }
}
window.history.pushState = resetHistoryFun('pushState') // 覆盖原来的pushState方法
window.history.replaceState = resetHistoryFun('replaceState') // 覆盖原来的replaceState方法

window.addEventListener('pushstate', reportBothEvent)          
window.addEventListener('replacestate', reportBothEvent)

2规则二界定——怎么判断页面失去焦点得到焦点

失去焦点得到焦点。我们主要进行如下这两个事件的EventListener

引入JSSDK

window.addEventListener('focus', ()=>{
    console.log('页面得到焦点')
});

window.addEventListener('blur', ()=>{
    console.log('页面失去焦点')
})

3规则三界定——怎么判断浏览器tab切换离开切换回来

tab切换离开切换回来。我们主要进行如下这一个事件的EventListener

引入JSSDK

document.addEventListener('visibilitychange',  () => {
    if(document.hidden) {
        console.log('页面离开')
    } else {
        console.log('页面进入')
    }
})

注意如果一个行为同时满足2个及2个以上的规则时只会取一个规则上报数据。避免不重复上报数据。

3.3 埋点设计

3.3.1 埋点个数

为了得到pv和uv的相关数据我们设计了2个埋点1个为页面进入时上报的埋点另外1个为页面离开时的埋点上报的数据都是一对的离开-进入页面为一对失去焦点-得到焦点为一对切换tab离开当前页面-返回当前页面也为一对

为什么要设计2个埋点设计2个埋点能覆盖全面上述我们所说的3种规则场景其次方面计算页面停留时长最后就是方便逻辑判断避免重复上报

3.3.2 参数的设计

按照不同的需求参数的设计分为如下4类

  • pvuv需要参数开发者传入参数unique_id——标识用户唯一标识、topic_id——当前网站唯一标识、current_env——当前网站环境默认为prod可用户传入

  • pvuv需要参数sdk内部获取参数duration——页面停留时长、last_page_url——上个页面url、page_url——当前页面url

  • SDK需要的参数帮助判断事件触发类型SDK内部获取参数eventType

  • 用户其他需要补充的参数自定义参数

3.4 数据上报

数据上报方式是XMLHttpRequest、window.navigator.sendBeacon基于h5sdk上报逻辑架构。

图4

3.5 兼容性和容错性

关于兼容性依赖于window对象、不兼容IE6、IE7IE8;

关于容错性对通用化内部逻辑做了try catch的容错兼容保证出错时不影响业务主逻辑运行同时上报一个出错的事件类型知道出错的原因以便提前做好对应的优化方案。

3.6 个人数据保护合规

为了保护好用户的个人数据及其隐私并满足法律法规要求在埋点的设计、采集、使用等环节需要进行充分的隐私保护设计。例如在埋点设计阶段需要确定标识符的选择、埋点参数的最小必要、采集频率的最小必要等在埋点的采集、使用阶段需要确保相关处理行为的透明、可控包括对用户进行告知获取用户的有效同意提供撤回同意的渠道等等。

四、数仓方案

埋点方案已经具备接下来的工作就是设计一套接入高效拓展便捷的数仓分析模型为实现以上既定的分析目标模型设计过程中需要解决以下核心问题。

4.1 核心问题列表

4.2 模型分层标准

介绍模型设计前先说下vivo 数仓模型分层基本原则及本次模型分层思路各层模型设计原则参照《vivo中台数仓建设方法论》层级设计摘要如下

4.3 模型层级架构

通过核心问题拆解发现为实现通用分析模型方案需要从数据接入层收口在数据接入时统一参数解析统一字段命名并设置相应的应用id字段区分各个业务数据源接着需要生成活跃数据明细表可统计相应的基础分析页面分析指标同时为满足留存分析的需要我们需要构建相应的活跃全量表留存分析主题表基于活跃增量表和活跃全量表生成用户活跃信息通过打标签的方式标记。至此涉及三个主题分析的模型规划完毕。层级划分原则及规划逻辑模型明细如图5

图5

从分层架构图可看出H5通用分析模型分为明细层(dw)、轻度汇总层(dma)、分析主题表 (dmt) 和指标层(da); 其中轻度汇总层可作为中间数据提供行业分析师及数据开发、业务产品等查询分析使用汇总层作为分析平台通用分析模型报表数据源导入mysql存储前端基于mysql表实现数据展示,各个模型设计细则如下

数据模型规划及设计的核心在于三点确定appid和用户id映射关系留存方案实现及留存记录入库bitmap方式读写。

1、确定appid和用户id映射关系-unique_id 关联设计

多业务id统一

## 明细层收口数据,统一id字段
SELECT  xx
       ,xx1
       ,CASE WHEN appid IN(1)       THEN  1   
             WHEN appid IN(2)   THEN  2  
             WHEN appid IN(3)       THEN  3    
             WHEN appid IN(4,5,6,...)       THEN  4      
        ELSE 0  END   AS  id_flag   
       ,CASE WHEN appid IN(1)      THEN  id1
             WHEN appid IN(2)  THEN  id2
             WHEN appid IN(3)      THEN  id3     
             WHEN appid IN(4,5,6,...)  THEN  IF(NVL(params['id1'],'')='',NVL(params['id2'],'NA'),params['id1'])            
       ELSE 'NA' END  AS  unique_id        
       ,appid        
  FROM ods_table_name_XXX  a     -- 各个接入业务线数据源 ods
 WHERE day='${today}'
   AND hour = '${etl_hour}'
   -- APPID 和 事件id 要匹配新增
   AND appid  in (1,2,3 ...)
   AND 事件id  in (XXX|167,XXX|168,...);

## id字段后续关联使用方式
## 增量关联全量确定是否新用户
SELECT if(b.unique_id is null,1,0) AS is_new
FROM
(
SELECT *
  FROM table_XXX_hi
 WHERE day= '${today}'
   AND hour = '${etl_hour}'
 GROUP BY XX
       ) a
       -- 取全量表唯一 unique_id 作为关联条件判断新老用户
       -- 新用户是相对于历史全量的
  LEFT JOIN ( SELECT unique_id,appid
                FROM      
               ( SELECT unique_id
                        ,appid
                        ,row_number() over(partition by unique_id,appid order by 活跃日期 asc)  as rn_0
                   FROM table_XXX_df
                  WHERE day='${etl_date}'
                ) a
                WHERE rn_0 = 1
             ) b
      ON a.unique_id = b.unique_id AND a.appid = b.appid;

2、留存方案实现及留存记录入库bitmap方式读写

留存方案

## 利用bitmap思想留存标签满8位转化为16进制组合到retain_tag之前这样可以利用很少的位数记录较长的活跃情况
## 示例代码如下
SELECT user_unique_id
       ,if(length(tmp_retain_tag) = 8,is_active,concat(is_active,tmp_retain_tag))          as tmp_retain_tag
       --  如果tmp_retain_tag长度为8的时候将数据转化为十六进制添加到retain_tag前并将本字段清空从头开始计数
       ,if(length(tmp_retain_tag) = 8,concat(con_tmp_retain_tag,retain_tag),retain_tag)    as retain_tag
       ,is_active
  FROM
(
    SELECT unique_id
            -- 前一天的临时存储与con_tmp_retain_tag保持一致
           ,tmp_retain_tag                            
            -- 如果转换为十六进制后的长度不为2则在左边添加0          
           ,if(length(conv(tmp_retain_tag,2,16)) = 2,conv(tmp_retain_tag,2,16),concat('0',conv(tmp_retain_tag,2,16))) as con_tmp_retain_tag 
            -- 历史轨迹
           ,retain_tag                                                                                      
           ,first_value(is_active) over(partition by unique_id,appid,topic_id  order by first_active_day desc)  as is_active
       FROM
      ( SELECT unique_id
               ,topic_id
               ,appid 
               ,first_active_day
               ,last_active_day
               -- 留存标签
               ,'0' as is_active
               ,tmp_retain_tag -- 形如 11101010
               ,retain_tag       -- 形如 A0E3
          FROM table_active_XX_df  -- 活跃全量表
         WHERE day= '${last_etl_date}'
         UNION ALL
        SELECT unique_id
               ,topic_id
               ,appid 
               ,day as first_active_day
               ,day as last_active_day
               -- 留存标签           
               ,'1' as is_active
               ,''  as tmp_retain_tag
               ,''  as retain_tag
          FROM table_active_XX_hi  -- 活跃明细表
         WHERE day= '${etl_date}'
         ) a
) b
WHERE rn =1;

## 留存指标统计## 以3日内及第3日留存为例
WITH tmp_table AS (
SELECT DAY
       ,unique_id
       ,appid
       ,首次活跃日期
       ,CONCAT(tmp_retain_tag,retain_tag) AS login_trace    
   FROM (
        SELECT DAY
           ,unique_id
           ,tmp_retain_tag
               ,appid
               ,首次活跃日期
               ,IF(nvl(retain_tag,'') <> '',CONV(SUBSTR(retain_tag,1,8),16,2),'') AS retain_tag
              -- 如果retain_tag为空时直接取空值。如果长度超过8位数取最后八位数如果长度不超过8位数取全部。如果是30日内新用户长度不超过8位
          FROM table_active_XX_df WHERE DAY = 统计日
        ) x1
)
## 以3日内及第3日留存为例
SELECT -- 第N日留存指标第N日来访
       ,SUM(IF(SUBSTR(login_trace,3,1) = '1',1,null))    AS retain_cnt_3th
       -- N日内留存指标:N日内访问过1次或N次
       ,SUM(IF(instr(SUBSTR(login_trace,2,2) ,'1')= 0,null,1))  AS retain_cnt_between_3th
 FROM (
       SELECT '统计日-2天' AS dt
              ,unique_id
              ,REVERSE(SUBSTR(login_trace,1,3)) AS login_trace
              ,appid
         FROM tmp_table WHERE SUBSTR(login_trace,3,1) = '1' AND 首次活跃日期 = 统计日-2天
) X GROUP BY dt,appid;

4.4 模型数据流图

至此模型的设计落地全部完成模型包含埋点数据表2张dw明细层模型1张维表1张dma轻度汇总主题层2张dmt主题表2张任务层深4层模型层2层模型数据接入0.5人日可完成。

数据流图如下

图6

五、数据展示

模型数据展示可基于用户行为分析平台数据指标存储使用 MySQL 数据库数据展示逻辑实现如下

图7

5.1 报表展示

报表配置完成后各个分析模块的前台展示示例如下

图8应用概况报表

图9用户留存报表

图10页面分析报表

六、未来展望

至此H5通用分模型落地流程已介绍完毕。本文主要是基于业务初期诉求快速落地通用的、统一的数据解决方案满足业务分析人员在产品初期最迫切的分析需求。随着业务的不断发展迭代运营产品的分析方向也会不断的扩展和深入同时不同的业务关注点不同针对分析模型的诉求也不尽相同。例如在业务中后期简单的访问留存分析已经支撑不了更进一步的决策制定此时针对页面访问的路径分析模型针对营销分析的订单转化模型、归因分析模型针对页面跳转分析的用户漏斗模型等需求会相应变多。

所以为更好的支撑业务目标达成H5通用分析模型系列在后期会根据业务诉求落地相应的分析模型持续为产品运营提供高效稳定的数据解决方案。

相关文章

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6