还原现场 — 前端录制用户行为技术方案

发表于 1年以前  | 总阅读数:511 次

一、问题背景

目前,在我们的项目中通常会使用各种各样的埋点和监控来收集页面访问的信息,例如点击埋点、PV埋点等,这些埋点数据能够反应绝大部分的用户行为,但是对于一些关注上下文的使用场景而言这些埋点是不够的。

  • 对于产品而言,通过点击或PV埋点来判断功能的使用情况有时候不够的,通常需要知道用户的真实使用路径以判断用户使用是否与功能设计所预想的保持一致,从而能够更好地分析用户使用情况并进一步优化和推广。
  • 对于开发而言,当我们收到系统异常通知的时候,监控系统只能告诉你系统出现了错误,但是不能给出错误的复现路径,对于稳定复现的错误而言还好,但对于偶发错误或复现路径隐藏较深的场景我们就较难去解决问题。
  • 对于测试而言,用户反馈线上bug的时候,首先需要知道的就是通过什么操作触发了这个问题,有时候用户自己可能也无法二次复现,这样的错误我们也无法通过埋点的数据去推导上下文,因此就会产生较大的沟通成本,在执行测试计划反馈 bug 时同理。
  • ......

因此,我们需要一种手段来获取用户某一时段连续的操作行为,也就是录制用户行为,包括整个会话中的每一个点击、滑动、输入等行为,同时支持回放录制的操作行为,完整且真实地重现用户行为以帮助我们回溯或分析某些使用场景。

二、技术方案

2.1 视频录制

录制用户行为最容易想到的就是将屏幕操作通过视频的方式录制下来,目前浏览器本身已经提供了一套基于音视轨的实时数据流传输方案 WebRTC(Web Real-Time Communications),在我们的录屏使用场景主要关注以下几个 API:

  • getDisplayMedia() - 提示用户给予使用媒体输入的许可从而获取屏幕的流;
  • MediaRecorder() - 生成对指定的媒体流进行录制的 MediaRecorder 对象;
  • ondataavailable - 当 MediaRecorder 将媒体数据传递到应用程序以供使用时将触发该事件;

整体录制流程如下:

  1. 调用mediaDevices.getDisplayMedia()由用户授权选择屏幕进行录制,获取到数据流;
  2. 生成一个new MediaRecorder()对象录制获取的屏幕的数据流;
  3. 在 MediaRecorder 对象上设置ondataavailable监听事件用于获取录制的 Blob 数据。
<template>
  <video ref="playerRef"></video>
  <button @click="handleStart">开启录制</button>
  <button @click="handlePause">暂停录制</button>
  <button @click="handleResume">继续录制</button>
  <button @click="handleStop">结束录制</button>
  <button @click="handleReplay">播放录制</button>
  <button @click="handleReset">重置内容</button>
</template>

<script lang="ts" setup>
import { ref, reactive } from 'vue';

const playerRef = ref();
const state = reactive({
  mediaRecorder: null as null | MediaRecorder,
  blobs: [] as Blob[],
});

// 开始录制
const handleStart = async () => {
  const stream = await navigator.mediaDevices.getDisplayMedia();
  state.mediaRecorder = new MediaRecorder(stream, {
    mimeType: 'video/webm',
  });
  state.mediaRecorder.addEventListener('dataavailable', (e: BlobEvent) => {
    state.blobs.push(e.data);
  });
  state.mediaRecorder?.start();
};
// canvas录制(特殊处理)
const handleCanvasRecord = () => {
  const stream = canvas.captureStream(60); // 60 FPS recording
  const recorder = new MediaRecorder(stream, {
    mimeType: 'video/webm;codecs=vp9',
  });
  recorder.ondataavailable = (e) => {
    state.blobs.push(e.data);
  };
}
// 暂停录制
const handlePause = () => { state.mediaRecorder?.pause() };
// 继续录制
const handleResume = () => { state.mediaRecorder?.resume() };
// 停止录制
const handleStop = () => { state.mediaRecorder?.stop() };
// 播放录制
const handleReplay = () => {
  if (state.blobs.length === 0 || !playerRef.value) return;
  const blob = new Blob(state.blobs, { type: 'video/webm' });
  playerRef.value.src = URL.createObjectURL(blob);
  playerRef.value.play();
};

const handleReset = () => {
  state.blobs = [];
  state.mediaRecorder = null;
  playerRef.value.src = null;
};
const handleDownload = () => {
  if (state.blobs.length === 0) return;
  const blob = new Blob(state.blobs, { type: 'video/webm' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.style.display = 'none';
  a.download = 'record.webm';
  a.click();
};
</script>

尽管浏览器原生提供了这样既简单又实用的屏幕录制解决方案,但在我们实际应用场景中仍旧有非常多的问题:

  1. 由用户感知并控制:通过 WebRTC 提供的 API 所实现的用户行为录制在开始录制前会通过弹窗来让用户完成对所需录制屏幕的授权,所有的录制行为均由用户自主控制,这种让用户感知到系统录制的方式对于我们预期的使用而言是不合适的,我们预期的录制行为对于用户而言应该是无感的,这种技术方案更适用于类似啄木鸟这种反馈系统由用户主动上报问题的场景或者考试系统屏幕监控、在线面试屏幕共享等等。
  2. 录制数据无法脱敏:视频录制过程中直接就将整个页面的内容录制下来,对于一些敏感的数据同样也会直接录制下来,在录制的过程中我们无法进行脱敏,这对于一些数据安全要求比较高或者涉及用户隐私的场景就不适用了。
  3. WebRTC 兼容性:在实现录制过程中使用的几个 WebRTC API 都具有一定的兼容性要求,不同的浏览器的支持情况各不相同,具体可进行相应的兼容性查询。

2.2 页面截图

众所周知,视频是由一帧帧的画面组合而成的,因此我们可以按照一定时间间隔来截图的方式保存当前页面快照,然后将快照按照相同的截取速度播放形成视频就能实现用户行为录制了。最常用的截图方法就是以 html2canvas 库为代表的 canvas 截图,我们在使用过程中也发现了较多问题:

  1. canvas 截图有较多局限之处,例如无法绘制动画、样式错位、不支持部分CSS样式等;
  2. 截图性能开销较大,可能会导致掉帧,例如我们在尝试中 css 动画有非常明显的卡顿等;
  3. 截图资源体积大,我们尝试中截图时单张图片体积为200k左右,以24帧来算一分钟录制的图片体积将近300MB,对带宽和资源存储都是浪费;
  4. 在需要忽略的元素上增加 data-html2canvas-ignore 属性或者设置 ignoreElements 属性删除特定元素可以对某些特定数据或内容进行脱敏,但会直接删除元素无法做到“有占位但无内容”效果,影响页面布局。
<template>
  <el-button @click="handleStart">开启录制</el-button>
  <el-button @click="handleStop">停止录制</el-button>
  <el-button @click="handleReplay">播放录制</el-button>
  <img :src="state.imgs[state.num ?? 0]" />
</template>

<script lang="ts" setup>
import { reactive } from 'vue';
import html2canvas from 'html2canvas';

const state = reactive({
  visible: false,
  imgs: [] as string[],
  num: 0,
  recordInterval: null as any,
  replayInterval: null as any,
});

const FPS = 30;
const interval = 1000 / FPS;
const handleStart = async () => {
  handleReset();
  state.recordInterval = setInterval(() => {
    if (state.imgs.length > 100) {
      handleStop();
      return;
    }
    html2canvas(document.body).then((canvas: any) => {
      const img = canvas.toDataURL();
      state.imgs.push(img);
    });
  }, interval);
};

const handleStop = () => {
  state.recordInterval && clearInterval(state.recordInterval);
};

const handleReplay = async () => {
  state.recordInterval && clearInterval(state.recordInterval);
  state.num = 0;
  state.visible = true;
  state.replayInterval = setInterval(() => {
    if (state.num >= state.imgs.length - 1) {
      clearInterval(state.replayInterval);
      return;
    }
    state.num++;
  }, interval);
};

const handleReset = () => {
  state.imgs = [];
  state.recordInterval = null;
  state.replayInterval = null;
  state.num = 0;
};
</script>

实际内容截图效果!

2.3 Dom 快照录制

每一个瞬间我们看到的页面都是浏览器当前渲染的 DOM节点,那么我们完全可以将 DOM 节点保存下来,并持续记录 DOM 节点的变化,然后再将记录的 DOM 节点数据通过浏览器渲染回放,这样即可实现用户行为录制的需求。

整个思路非常简单,但具体实现起来是非常复杂的事情,我们需要考虑 DOM 节点数据如何保存、如何捕获用户行为并记录 DOM 节点变换和如何将记录的数据在浏览器上回放出来等。

所幸当前社区已经有非常成熟的库,也就是 rrweb(record and replay the web)

rrweb 主要由 3 部分组成:

  1. rrweb-snapshot,包含 snapshot 和 rebuild 两部分,snapshot 用于将 DOM 及其状态转化为可序列化的数据结构并添加唯一标识,rebuild 是将 snapshot 记录的数据结构重建为对应 DOM。
  2. rrweb,包含 record 和 replay 两个功能,record 用于记录 DOM 中的所有变更,replay 则是将记录的变更按照对应的时间一一重放。
  3. rrweb-player,为 rrweb 提供一套 UI 控件,提供基于 GUI 的暂停、快进、拖拽至任意时间点播放等功能。

录制过程

rrweb 在录制时会首先进行首屏 DOM 快照,遍历整个页面的 DOM Tree 并通过 nodeType 映射转换为 JSON 结构数据。针对不同 nodeType 类型的节点类型的序列化操作具有非常多的细节,如想了解细节可阅读这部分源码。全量快照数据示例如下:

在获取首屏全量快照之后,我们就需要监听各类变动以获取增量的数据,增量改变的数据也需要同步转换为 JSON 数据进行存储。对于增量数据更新,则是通过 mutationObserver 获取 DOM 增量变化,以及通过全局事件监听、事件(属性)代理的方式进行方法(属性)劫持,并将劫持到的增量变化数据存入 JSON 数据中。针对不同类型的变动有这不同的监听处理方式,如想了解细节可阅读这部分源码。

回放过程

回放主要就是将录制过程中的全量快照和增量快照进行重建复原,那么为保证一个安全可靠的环境在回放时我们不应该执行被录制页面中的 JavaScript,在重建快照的过程中通过 script 标签改写为 noscript 标签和 dom 重建在 iframe 中并设置 sandbox 属性等手段来构建安全可靠的沙盒环境。在沙盒环境中,首先需要对首屏 DOM 快照进行重建,遍历 JSON 产物的同时通过自定义 type 映射到不同的节点构建方法以重建首屏 DOM 结构,然后 rrweb 内部则根据不同的增量类型调用不同的函数在页面对增量数据进行展现。同时,播放时通过录制产生的时间戳来保证回放顺序,通过 Node id 作用至指定的 DOM 节点,通过 requestAnimationFrame 保证页面播放流畅度。

rrweb 实现原理可参考以下相关资料以及源码/官方文档:

  • rrweb:打开 web 页面录制与回放的黑盒子 - 知乎
  • 神策数据王磊:如何用 JS 实现页面录制与回放 - 掘金
  • rrweb录屏原理浅析 - 掘金
  • rrweb 实现原理介绍 - 微信公众号
  • rrweb 带你还原问题现场 - 云音乐大前端
<template>
  <button @click="handleStart">开启录制</button>
  <button @click="handleStop">结束录制</button>
  <button @click="handleReplay">播放录制</button>
  <div class="replay" ref="replayRef"></div>
</template>

<script lang="ts" setup>
import { reactive, ref } from 'vue';
import * as rrweb from 'rrweb';
import rrwebPlayer from 'rrweb-player';
import 'rrweb-player/dist/style.css';

const replayRef = ref();
const state = reactive({
  events: [] as any[],
  stopFn: null as any,
});

const handleStart = () => {
  state.stopFn = rrweb.record({
    emit(event) {
      if (state.events.length > 100) {
        // 当事件数量大于 100 时停止录制
        handleStop();
      } else {
        state.events.push(event);
      }
    },
  });
  ElMessage('开始录制');
};

const handleStop = () => {
  state.stopFn?.();
  ElMessage('已停止录制');
};

const handleReplay = () => {
  new rrwebPlayer({
    target: replayRef.value, // 可以自定义 DOM 元素
    // 配置项
    props: {
      events: state.events,
    },
  });
};
</script>

从效果上来讲,rrweb 录制内容存储了完整的页面结构能够较好地还原页面的整个操作,并且 rrweb 录制无损录制具有较好的清晰度,不像视频录制和页面截图需要考虑分辨率与产物大小的问题,同时也不像 canvas 截图一样对内容和样式有较大的局限性使得部分页面内容无法录制。

从性能上来讲,rrweb 录制传输的内容为 JSON 数据并且只对用户操作内容作增量记录,当页面静默的时候不会有额外数据的记录,相比较视频录制和页面截图而言大大减少了最终产物的体积,减轻了数据传输的压力,同时也提高了录制的性能。

从功能上来讲,rrweb 除了基础的录制回放功能之外,还具有较好的可扩展性和可操作性:

  • rrweb 录制得到的产物是 JSON 数据,还支持将 JSON 数据转成视频,工具rrvideo
  • 其允许通过配置来进行数据脱敏,不录制某些元素或屏蔽某些事件,详见链接
  • 其还提供了存储优化的策略以减少录制的数据量,详见链接
  • 其还支持自定义插件来进行扩展,详见链接

2.4 方案对比

对比内容视频录制页面截图Dom 快照录制
开源库WebRTC 原生支持html2canvasrrweb
用户感知录制有感录制无感录制无感
产物大小相对较小
兼容性详见相关 API 兼容性部分场景内容截图无法显示兼容性相对较好
可操作性强(支持数据脱敏/加密等)
回放清晰度录制时决定,有损录制录制时决定,有损录制高保真

三、应用场景

在获取页面的录屏内容之后,这只是第一步,更重要的是我们能够利用这些录屏信息获取到什么信息,分析出什么内容?

#应用场景说明1产品功能分析产品在功能的上线后仅通过点击或PV埋点来判断使用情况是不够的,更应该关注于一些关键页面/功能的真实使用场景,通过用户行为录制将用户的操作路径记录下来,通过回放种子用户的操作进行分析或利用算法对使用路径进行分析能够更好了解功能设计的情况,并帮助进一步优化。2用户访谈记录产品在对用户进行访谈时通过整理用户口述记录和回放访谈录音等方式来进行分析和研究,整体访谈的成本较高,信息利用率也较低,而通过用户行为录制记录下来访谈过程中用户真实的操作记录能够更好地帮助产品来回顾访谈用户的操作习惯。3问题现场复现解决问题第一步就需要复现问题,但有时候问题的复现操作是极其隐蔽的或者由于用户的使用环境等因素很难定位,通过录制用户行为来保存报错时刻的上下文使得我们能够更好地了解用户报错的操作,最大程度减少沟通和内容传递的成本。4自动化测试用例通常自动化测试用例的编写和维护都由人工手动来完成,成本相对比较高,后期维护也不方便,通过用户行为录制将录制的数据加以转换就可以更加快捷方便地进行测试用例的采集,同时也便于管理。5其他还有案例复盘、行为监控/监管、业务流程质检...

四、平台方案

Sentry 平台 提供了录制与回放功能用于进行分析,其应用重点在于查看错误或性能问题发生之前、期间和之后的操作情况,其录制与回放功能同样基于 rrweb 库进行开发。

Hotjar 平台提供了录制与回放功能用于进行分析,其除了录制与回放功能外,其提供了页面热力图等分析能力,以帮助用户更好地了解产品的情况,官方也提供了 Demo 可体验 。

其他相关的商业化平台还有LogRocket、FullStory、marker.io等等。

五、总结

目前,用户行为录制已经在各个场景中广泛使用,例如用户调研、产品分析、Bug回溯、自动化测试和行为监控等等。视频录制、页面截图和 Dom 快照录制等技术方案各有优劣,在面对不同的使用场景时要选择合适的技术方案,但总体而言,以 rrweb 库为代表的 Dom 快照录制技术是目前最为广泛使用也最具优势的技术方案,在各种商业化解决方案中也主要采用该技术方案或思路来实现。

参考资料

  • 用户行为录制技术方案 - 掘金
  • 前端录制回放系统初体验 - 掘金
  • 浏览器录制方案的调研和总结 - 知乎
  • 快速入门 WebRTC:屏幕和摄像头的录制、回放、下载 - 掘金

本文由哈喽比特于1年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/kdbhD01m_xgb9jnk5uvtew

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
为Electron程序添加运行时日志 5年以前  |  20342次阅读
Node.js下通过配置host访问URL 5年以前  |  5894次阅读
用 esbuild 让你的构建压缩性能翻倍 4年以前  |  5786次阅读
 目录