37DATA

浅谈统计页面访问时长方案【单页面时长篇】

前言

       说起页面访问时长,在如今的大数据时代,不乏许多产品需要去统计用户在当前页面停留的时长长短去推测用户的喜好等相关信息,进而去给用户进行相应的消息推送,当然,这些数据肯定不当当只是用在这些层面,在这里我就不过多细谈了,毕竟自己岗位不是特别的关注数据。
刚好最近的易览项目需求统计到各个页面的访问时长及总时长,这里就网上大多的实现方式以及自己最终制定的统计方案聊一聊前端要如何去统计的页面的访问时长,当然由于易览移动端主要嵌在企业微信里,受其APP Webview部分内置机制的影响,目前仍存在部分无解的兼容问题,这个我们后续再谈。

一、什么是页面访问时长?哪些时长应该算进去?

        最最最直白的来说,狭义上就是用户浏览从进入该页面进行浏览到关闭该页面的这段时间间隔就是该页面的页面访问时长,这里我为啥要标红关闭两字呢,原因是我们将浏览的页面切换至其他标签页时,那么此时页面的访问时长应该暂停统计,当用户切回来之后则进行进行,直至用户将页面关闭,所以当你将页面切至后台时,也就是页面没有展现在你的视角前面,这些时间间隔是不能算进页面的访问时长里的。

Image

你正在浏览的网页


        为什么说上面说的访问时长是狭义上的,主要考虑到的是很多时候用户访问一个页面后,虽然页面呈现在屏幕上,但用户其实是将其挂在那里并没有去进行浏览,也就是我们常说的无操作状态即网页版的“挂机”,所以,这些时长数据往往并不是有效的数据,很多运营者还会加多一个无操作的上限时间间隔,在超过这个时间间隔仍对页面无操作时,是需要将这部分时长给抛弃的,不并入页面的访问时长,即当无效时长处理。

二、如何计算页面访问时长

        说了这么多,进入正题,首先我们将页面时长分为

  • 从打开到关闭 -> 页面访问总时长

  • 页面切换至后台 -> 页面非活跃时长

  • 页面无操作时间上限 -> 无操作上限时长

  • 页面每次打开后切至后台或打开后台直接关闭的时长 -> 页面单轮活跃时长


        那
        页面访问实际时长 = 页面访问总时长 - 页面非活跃时长(总和) - 页面无操作上限时长(如果期间有无操作时间超过无操作上限时长)
        或
        页面访问实际时长 = 页面单轮活跃时长 x N(轮) - 页面无操作上限时长(如果期间有无操作时间超过无操作上限时长)
      【其实第二种就是非活跃暂停,活跃时继续累加】

        围绕这俩公式我们一起进入下面的讲解

三、什么行为算是操作状态

        通常来说,页面的操作行为包括用户的鼠标相关事件(点击、移动)、页面的滚动、页面的聚焦、键盘的键入以及手指的触控(触控屏或移动端),所以,只要浏览时用户触发了上面任一操作,就认为页面是处于一个操作状态的,如果是在自己定义的无操作上限时长内未执行过上述任一操作的,即可以当无效访问/浏览。
        所以,如何判断用户在页面无操作上限时长内有无执行操作,你便可以给页面绑定这些事件并设置一个无操作计时器①,每逢触发这些事件时你就将①重新计时,如果①计时到了无操作上限时长,那么就可以认为之前的无操作时间对页面访问时长来说是无效时长,可以减去。讲到这里,上面公式的“- 页面无操作上限时长”不就计算出来了。

Image

这是我目前的操作绑定,这里我把聚焦事件排除,感觉没用上,视哪些行为为操作行为自己最终要根据自己项目的要求自定,但大部分都是上述这些操作事件。

四、什么是页面非活跃状态或失活状态

        页面非活跃状态,即页面被我们挂起或切至隐藏、后台的状态,举几个例子
        1、PC端新窗口打开新页面,旧页面被置到底部,被新页面窗口顶替
        2、PC端打开个新的标签页(包括右键新打开标签页),旧页面被切走
        3、PC端将页面缩小至任务栏
        4、iframe内嵌页面隐藏
        5、移动端将页面切至后台
        6、移动端新开页面窗口顶替旧页面窗口
        7、移动端访问页面时锁/息屏【但由于JS监听不了手机的锁屏,这个时长无法得知】


        以上这些就是页面的非活跃状态又或称页面的一个失活状态,这期间的时间间隔是不能被算进页面的访问时长里的。
目前,JavaScript可以通过 结合 document.hidden 与 visibilitychange 、pagehide 去监听页面被活跃与非活跃状态互相切换的时机,其中建议采取前者的方式,两者兼容性都不是很好,特别是在ios端,但后者目前的兼容性相对前者更差,所以建议采用前者。

Image

 同时,针对上面的第四点iframe隐藏的情况,可通过让其在隐藏利用H5postMessage进行广播,在父容器进行监听得知。那么你就可以计算出非活跃的时长,在后续计算访问时长时进行删减,或者说在进入非活跃状态时你将统计时长的计时器进行一个暂停,这样就可以算出一轮又一轮的有效时长,最后再进行一波相加。

Image

绑定visibilitychange事件

Image

触发visibilitychange事件判断页面显示状态

五、实现思路及方式

        讲完各个时长的概览及获取时机后,我们要如何在抵达时机时去计算又或者是记录它们呢,这里给大家个五分钟时钟时间,先别看下面的思路,假设页面关闭时会触发一个关闭事件并将最终结果提交,自己想一下自己要怎么去计算这些时长。

好了,公布下我自己的思路吧,往大的讲有两种:

  • 一是采用计时器的方式【简单粗暴但耗性能】②

  • 二是采用取首末时间互减计算的方式【推荐,但受用户调本地时间影响】③

再往细的分的话上面两种思路都可以再细分为2种方式

  • 一是计算非活跃时长,在最后计算实际时长用总时长去减去非活跃总时长【总的 - 无效的】④

  • 二是非活跃状态时暂停计时,将前面统计到时长记录,激活后进行累加【有效的 x N块】⑤

六、统计流程图

Image

采用②④方式

Image

采用②⑤方式

Image

采用③④方式

Image

采用③⑤方式

七、正常关闭以及异常关闭

        好了,看到这里,一个大体的访问时长统计方案思路我想大家都差不多一知半解了,现在我来讲讲最后的关闭提交逻辑,刚刚我们不是假设关闭会触发一个事件么,那这个事件是什么?

Image

它就是 beforeunload 事件,PC端其会在页面正常关闭前触发,在移动端,关闭页面会同时触发 visibilitychange 、 pagehide 及 beforeunload 事件,beforeunload最后触发,当然前提是手机支持后两种事件,目前 beforeunload 在PC端的兼容性比较好,移动端参差不齐,所以在统计单页面访问时长的时候建议触发准备进入非活跃状态时,就将统计到的时长进行一次提交,而不是直到整个页面关闭执行beforeunload才提交,毕竟有的机子不支持嘛。

        那这时如果遇到像浏览器崩溃闪退、浏览器被任务管理器强制关闭、电脑手机死机重启等使页面异常关闭的情况,直接没有触发的 beforeunload 事件又或者 visibilitychange 事件时,我又得如何提交 or 直接将此数据抛弃?no no no

八、心跳机制【心跳计时器】

        什么是心跳机制,它是一种用来防范统计访问时长时出现异常关闭的机制,正如其别称心跳计时器,其原理就是定义一个计时器,按指定的频率定时地将已统计到的访问时长数据传至后台服务器,当页面异常关闭时,心跳计时器肯定也是一起被销毁的,所以后台服务器在没接收到带有最终标志的时长数据且在指定的时间频率内没有再次监听到心跳请求时,则认为页面被异常关闭,将最后一次心跳上传的时长数据作为最后的数据,你可能会说这样的误差很大,但这是用于防范异常情况的方法,总好过直接把数据抛弃了。

Image

PS:考虑到有的用户习惯将浏览器直接置于后台并将电脑休眠,由于计时器在后台仍是会工作的,那么心跳请求可能会持续好几天,所以建议在切换为非活跃状态时,告诉后台服务器暂停对该页面心跳请求的监听,等重新激活时再请求后台监听心跳,防止不必要的性能资源浪费

九、正常退出标识

Image

        如果你在统计时长的时候会定时地去将已统计到时长保存到本地localstorage的话,那你还可以额外保存多一个正常退出标识,就是本地缓存再去设置多一个标识变量(我这里起名为exit),默认值为“pending”,唯有页面触发 beforeunload 事件时,我才会将其置为true,代表关闭正常,那么下次激活此页面或者进入此页面,当判断到exit为true时即可认为页面上次是正常切换或关闭,再将其重置为“pending”并清空之前保存的时长数据,否则,如果判断其仍为“pending”状态,则认为上次是异常关闭,你可以选择在上次的状态继续统计下去或者抛弃并开启新的一轮。

十、结语

        好了以上就是自己制定的关于统计单页面访问时长的一个方案,当然笔者这里还有些无解的兼容问题想请教下读者,由于目前此方案在一些特定的APP里仍存有部分非可控的兼容问题无法解决,想请教下有无较好的解决方法,目前此方案在企业微信里存在以下问题


       1、企微的webview关闭页面不会触发 visibilitychange 、 pagehide 及 beforeunload 三大事件,且JS-SDK文档无提供任何监听页面关闭的API方法,导致企微访问时无法进行一个关闭事件的监听,无法提交最终的访问时长;
       2、企微的webview会在页面切换至非活跃状态时拦截页面触发的ajax请求,导致无法进行切换非活跃状态时提交已统计时长的操作;
       3、企微的webview在切换新页面后会将旧页面的计时器操作冻结,重新激活浏览后无法重新设置新计时器,导致无法继续进行心跳计时器。


       如果有大牛有什么好的兼容方法又或者更好的方案的话欢迎探讨指教,谢谢!其实页面访问时长的统计方案相对来说还是比较容易实现,但关键在于其所需要的这些监听事件在目前许多浏览器系统中的兼容性还不够好,所以需要考虑比较多的特殊情况进行兼容,单页面目前还算好一点,多页面更可谓杂之又杂,由于篇幅限制,为了相对清晰一点,多页面的页面访问时长统计方案就放在下一篇再来谈啦。

       感谢您的阅读,如有发现错误,欢迎指正,谢谢!