第二章:学习行为数据采集
数据采集这事儿,说白了就是给系统装上一双眼睛。没有数据,你后面做再牛的算法都是空中楼阁。我在做在线教育系统的时候,最深的体会就是:数据质量决定了推荐系统的天花板。
这一章,我们聊聊怎么把这双眼睛装好。从埋点设计到数据清洗,一条线走通。
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 日志采集架构
别把日志直接写数据库,扛不住的。我常用的方案是:
- 应用服务器写本地文件
- Filebeat 或 Fluentd 采集
- 发送到 Kafka 缓冲
- 消费写入 Elasticsearch 或 HDFS
避坑指南:我曾经把日志直接写到 MySQL,结果高峰期把数据库打挂了。后来改成异步写日志,用消息队列缓冲,再也没出过问题。
2.4 数据清洗与预处理
原始数据很脏的。你想想看,用户可能乱点、网络可能丢包、埋点代码可能有 bug。所以清洗这步,不能省。
2.4.1 常见脏数据
| 类型 | 示例 | 处理方式 |
|---|---|---|
| 缺失值 | user_id 为空 | 丢弃或填充默认值 |
| 异常值 | 视频时长 999999 秒 | 按业务阈值过滤 |
| 重复数据 | 同一条日志出现两次 | 去重(基于 event_id) |
| 格式错误 | 时间戳格式不统一 | 统一转换为 UTC |
2.4.2 清洗流程
我一般分三步走:
- 格式校验:检查字段类型、长度、是否必填
- 业务过滤:剔除测试数据、爬虫数据
- 数据转换:统一时间格式、解析嵌套字段
# 数据清洗示例(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 本章知识体系
下面这张图,把整个数据采集的流程串起来了。从用户行为发生,到数据最终入库,每一步都有讲究。
数据采集是整个智能调仓系统的地基。地基打不好,上面盖的房子再漂亮也得塌。我个人建议,花 40% 的时间在数据采集和清洗上,后面建模会省心很多。
最后说一句:数据采集不是一次性工作。随着业务迭代,埋点要持续优化。我每个季度都会 review 一次埋点清单,砍掉没用的,补充新的。保持数据体系的健康度,比什么都重要。