第二章:学习行为数据采集

数据采集这事儿,说白了就是给系统装上一双眼睛。没有数据,你后面做再牛的算法都是空中楼阁。我在做在线教育系统的时候,最深的体会就是:数据质量决定了推荐系统的天花板

这一章,我们聊聊怎么把这双眼睛装好。从埋点设计到数据清洗,一条线走通。

2.1 埋点设计原则

埋点不是乱埋的。我见过不少团队,上来就埋几百个事件,结果数据全烂在仓库里。嗯,这里要注意:埋点设计要遵循三个核心原则

原则 说明 我的经验
最小化 只采集业务需要的数据 我曾经一个项目埋了200+事件,最后只用了30个
一致性 命名规范统一,字段类型固定 建议用 snake_case,比如 user_click
可追溯 每条数据都能追溯到用户行为 带上 session_id 和 timestamp

核心原则:埋点不是越多越好,而是越准越好。你想想看,数据仓库里堆满垃圾数据,最后清洗成本比采集成本还高。

2.2 前端行为数据采集

前端采集,主要抓用户和界面的交互。我个人习惯用 事件监听 + 自动埋点 的组合方式。

2.2.1 手动埋点 vs 自动埋点

手动埋点:在代码里显式调用采集函数。适合关键路径,比如「点击购买」。

自动埋点:通过 DOM 事件代理,采集所有点击行为。适合探索性分析。

我建议这样搭配:

  • 核心转化路径用 手动埋点,保证数据准确
  • 非关键路径用 自动埋点,降低开发成本

小技巧:自动埋点的时候,记得给 DOM 元素加上 data-track 属性。这样既能采集,又不会污染业务逻辑。

2.2.2 前端采集代码示例

// 手动埋点示例
function trackEvent(eventName, payload) {
  const data = {
    event: eventName,
    properties: payload,
    timestamp: Date.now(),
    session_id: getSessionId()
  };
  // 发送到后端
  navigator.sendBeacon('/api/track', JSON.stringify(data));
}

// 使用
trackEvent('video_play', {
  video_id: 'v123',
  duration: 120,
  from: 'recommend'
});

注意:前端采集不要用 XMLHttpRequest 同步发送,会阻塞页面。用 sendBeacon 或者图片打点的方式,性能更好。

2.3 后端日志采集

后端日志,主要记录服务器端的请求和响应。为什么需要后端日志?因为前端数据可能丢失——用户断网、浏览器崩溃,这些情况前端数据就没了。

我在项目中遇到过:前端采集显示用户看了视频,但后端日志显示根本没请求播放接口。后来发现是前端 SDK 的 bug。所以,前后端数据要互相校验

2.3.1 日志格式规范

我建议统一使用 JSON 格式,方便后续解析。字段包括:

  • timestamp:请求时间
  • method:HTTP 方法
  • path:请求路径
  • user_id:用户标识
  • params:请求参数
  • response_time:响应耗时
  • status_code:状态码
// 后端日志示例(Node.js)
{
  "timestamp": "2024-01-15T10:30:00Z",
  "method": "POST",
  "path": "/api/video/play",
  "user_id": "u12345",
  "params": {
    "video_id": "v123",
    "course_id": "c456"
  },
  "response_time": 234,
  "status_code": 200
}

2.3.2 日志采集架构

别把日志直接写数据库,扛不住的。我常用的方案是:

  1. 应用服务器写本地文件
  2. Filebeat 或 Fluentd 采集
  3. 发送到 Kafka 缓冲
  4. 消费写入 Elasticsearch 或 HDFS

避坑指南:我曾经把日志直接写到 MySQL,结果高峰期把数据库打挂了。后来改成异步写日志,用消息队列缓冲,再也没出过问题。

2.4 数据清洗与预处理

原始数据很脏的。你想想看,用户可能乱点、网络可能丢包、埋点代码可能有 bug。所以清洗这步,不能省。

2.4.1 常见脏数据

类型 示例 处理方式
缺失值 user_id 为空 丢弃或填充默认值
异常值 视频时长 999999 秒 按业务阈值过滤
重复数据 同一条日志出现两次 去重(基于 event_id)
格式错误 时间戳格式不统一 统一转换为 UTC

2.4.2 清洗流程

我一般分三步走:

  1. 格式校验:检查字段类型、长度、是否必填
  2. 业务过滤:剔除测试数据、爬虫数据
  3. 数据转换:统一时间格式、解析嵌套字段
# 数据清洗示例(Python)
import pandas as pd

def clean_logs(df):
    # 1. 去除缺失 user_id 的数据
    df = df.dropna(subset=['user_id'])
    
    # 2. 过滤异常时长(视频时长不超过 10 小时)
    df = df[df['duration'] <= 36000]
    
    # 3. 去重
    df = df.drop_duplicates(subset=['event_id'])
    
    # 4. 时间格式统一
    df['timestamp'] = pd.to_datetime(df['timestamp'], utc=True)
    
    return df

经验之谈:清洗逻辑一定要写单元测试。我吃过亏,改了一行清洗代码,结果把正常数据全过滤掉了。后来每条清洗规则都配了测试用例,再也没出过类似问题。

2.5 本章知识体系

下面这张图,把整个数据采集的流程串起来了。从用户行为发生,到数据最终入库,每一步都有讲究。

学习行为数据采集流程 用户行为 前端埋点采集 手动+自动 后端日志采集 JSON格式 数据清洗 去重/过滤/转换 数据存储 ES/HDFS/Kafka 前后端数据校验 交叉验证 核心原则:最小化、一致性、可追溯 清洗三步:格式校验 → 业务过滤 → 数据转换 前后端数据互相校验,确保数据完整性

数据采集是整个智能调仓系统的地基。地基打不好,上面盖的房子再漂亮也得塌。我个人建议,花 40% 的时间在数据采集和清洗上,后面建模会省心很多。

最后说一句:数据采集不是一次性工作。随着业务迭代,埋点要持续优化。我每个季度都会 review 一次埋点清单,砍掉没用的,补充新的。保持数据体系的健康度,比什么都重要。

蓝海数据掘金营,专注资料整理