第02章:第一方数据体系 — 访客、会话与身份识别的核心概念

第02章:第一方数据体系 — 访客、会话与身份识别的核心概念

“数据不会说谎,但如果你连同一个人的两次访问都无法识别为同一个人,那你的数据说的是一门没人能懂的外语。”


为什么身份识别是一切的基础

想象这样一个场景:一个用户周一看到你的 Facebook 广告,点进来看了一眼,没有提交表单,离开了。周三,他直接在 Google 搜索你的产品名,又进来了,这次提交了报价请求。

如果你的系统无法识别这两次访问来自同一个人,你会得出什么结论?

  • Facebook 广告带来了 1 次访问,0 次转化,ROI 为零
  • 直接搜索带来了 1 次转化
  • 结论:Facebook 广告没用,砍掉预算

但真相是:Facebook 广告是这次转化的触点之一,用户是在看过广告之后,主动去搜索的。如果你砍掉 Facebook,可能连"直接搜索"这个转化也会减少。

这就是为什么身份识别是整个归因体系的基础。没有准确的访客识别,归因分析就是建立在流沙之上。


三个核心概念:访客、会话、事件

在构建追踪系统之前,我们需要建立精确的概念框架。这个框架由三个层级构成:

身份识别层级体系
─────────────────────────────────────────────
访客(Visitor)
    │  持久化身份,跨时间、跨会话保持不变
    │  标识符:visitor_id(存储在 Cookie 中)
    │
    ├─ 会话 / 访问(Session / Visit)
    │       │  单次访问行为的容器
    │       │  标识符:visit_id(每次访问生成新的)
    │       │
    │       ├─ 事件(Event)
    │       │       │  会话内的具体行为
    │       │       │  例:页面浏览、按钮点击、表单提交
    │       │
    │       ├─ 事件
    │       └─ 事件
    │
    ├─ 会话(另一次访问)
    │       └─ 事件...
    │
    └─ 会话(又一次访问)
            └─ 事件...
─────────────────────────────────────────────

访客(Visitor):代表一个真实的人或设备。即使他们多次访问你的网站,visitor_id 保持不变。这让你能够追踪一个用户从第一次接触到最终转化的完整旅程。

会话/访问(Session/Visit):代表一次连续的访问行为。在我们的系统里,我们用 visit_id 来标识每一次访问,并记录这次访问的来源信息(UTM 参数、referrer 等)。一个 visitor_id 对应多个 visit_id。

事件(Event):会话内发生的具体行为。提交表单是一个事件,点击按钮是一个事件,浏览一个页面也是一个事件。在我们的 MVP 系统里,最关键的事件是"表单提交",对应到 wb_leads 表的一条记录。


visitor_id:跨会话的持久化身份

visitor_id 的核心设计需求是:同一个浏览器/设备的所有访问,应该使用相同的 visitor_id

实现方式通常是第一方 Cookie:

访客第一次访问
─────────────────────────────────────
1. 浏览器发出请求,没有 visitor_id Cookie
2. 落地页脚本检测到没有 visitor_id
3. 生成一个新的 UUID:visitor_id = "a3f2-..."
4. 将这个 UUID 写入第一方 Cookie:
   Set-Cookie: wb_vid=a3f2-...; max-age=63072000; SameSite=Lax
   (max-age = 2 年,以秒为单位)
5. 在 localStorage 里也备份一份(应对某些浏览器限制)

访客第二次访问(相同浏览器)
─────────────────────────────────────
1. 浏览器发出请求,带有 visitor_id Cookie
2. 落地页脚本读取 Cookie:visitor_id = "a3f2-..."
3. 继续使用这个 visitor_id,不生成新的
4. 这次访问会有新的 visit_id,但 visitor_id 相同

为什么用第一方 Cookie 而不是第三方 Cookie?

对比维度 第一方 Cookie 第三方 Cookie
设置域名 你自己的域名 第三方域名
浏览器限制 几乎不受限 正在被全面限制
Safari ITP 影响 最长 7 天(JS 设置)或更长(HTTP 设置) 已被完全阻止
跨网站追踪 仅限你的域名 可跨网站(但已失效)
隐私政策要求 需要用户同意 同样需要用户同意

关键细节:visitor_id Cookie 必须通过 HTTP 响应头设置,而不是 JavaScript,原因是:

Safari 的 ITP 对 JavaScript 写入的 Cookie 有 7 天有效期限制,但对 HTTP 响应头设置的 Cookie 可以延长到 1-2 年。这意味着如果你在前端 JS 里写 Cookie,iOS Safari 用户每 7 天就会生成一个新的 visitor_id,身份识别完全失效。

正确的方式是:前端把 visitor_id 传给你的 API,API 在响应里用 Set-Cookie 头设置。


visit_id:单次访问的唯一标识

每次用户访问你的落地页,都应该生成一个新的 visit_id。这个 ID 的作用是:

  1. 关联这次访问的所有数据:UTM 参数、referrer、fbclid、gclid 等,都记录在以 visit_id 为主键的 wb_visits 表里

  2. 连接访问和转化:当用户提交表单时,visit_id 会被写入 wb_leads 表,把这次转化和对应的广告来源关联起来

  3. 支持多触点归因:同一个 visitor_id 可以有多个 visit_id,这为"首次点击归因"、“末次点击归因”、"线性归因"等不同归因模型提供了数据基础

visit_id 的生成和存储逻辑:

页面加载时
─────────────────────────────────────
1. 生成 visit_id = UUID()
2. 存储在会话级别(sessionStorage 或内存变量中)
   (不写入持久化 Cookie,因为每次访问应该独立)
3. 发送 POST /api/visit,记录本次访问信息
4. visit_id 也存入一个 session-level Cookie,
   方便表单提交时自动携带

用户提交表单时
─────────────────────────────────────
1. 读取 visitor_id(来自持久化 Cookie)
2. 读取 visit_id(来自 session Cookie 或 JS 变量)
3. 读取 qualifier(来自 session Storage,本次 A/B 版本)
4. 一起提交到 POST /api/lead

身份识别的完整数据流

让我们把整个身份识别流程串联起来,看清楚数据是如何流动的:

用户点击广告 (带有 utm_source=facebook&fbclid=xxx)
        │
        ▼
浏览器加载落地页 URL
(https://yourdomain.com/landing?utm_source=facebook&fbclid=xxx)
        │
        ▼
前端 JavaScript 运行
        │
        ├─ 1. 读取 URL 参数
        │       utm_source=facebook
        │       utm_medium=cpc
        │       utm_campaign=insurance_q1
        │       fbclid=AQHxxxxxxxx
        │
        ├─ 2. 读取/生成 visitor_id
        │       检查 Cookie "wb_vid"
        │       → 存在:使用现有值
        │       → 不存在:生成新 UUID,临时存储
        │
        ├─ 3. 分配 qualifier (A/B 版本)
        │       检查 Cookie "wb_qual"
        │       → 存在:使用现有值(保证同访客看同版本)
        │       → 不存在:按权重随机分配 A/B/Control
        │
        ├─ 4. 生成 visit_id(每次访问新建)
        │       visit_id = UUID()
        │
        └─ 5. POST /api/visit
                {
                  visitor_id,
                  visit_id,
                  qualifier,
                  utm_source,
                  utm_medium,
                  utm_campaign,
                  fbclid,
                  landing_url,
                  referrer
                }
                │
                ▼
        服务端处理
                │
                ├─ 写入 wb_visits 表
                └─ 响应头设置持久化 Cookie
                   Set-Cookie: wb_vid=xxx; HttpOnly; max-age=63072000
                   Set-Cookie: wb_qual=A; max-age=2592000

多设备场景:身份识别的边界

一个现实问题:同一个用户在手机上看到广告,在电脑上提交表单,这两个 visitor_id 是不同的。

这是设备级别身份识别的固有局限性。要解决跨设备识别,需要引入"已知标识符":

设备级别识别(Cookie)
─────────────────────────────────────────────
手机浏览器:visitor_id = "aaa-111"
电脑浏览器:visitor_id = "bbb-222"
→ 无法关联,是两个独立访客

基于已知标识符的关联
─────────────────────────────────────────────
当用户在手机上用邮箱注册:
  email: "user@example.com" ←→ visitor_id = "aaa-111"

当用户在电脑上用同一邮箱登录/提交表单:
  email: "user@example.com" ←→ visitor_id = "bbb-222"

通过 email 可以把两个 visitor_id 关联起来

对于大多数 MVP 阶段的追踪系统,设备级别的识别已经够用了。跨设备追踪是进阶功能,等基础体系建立后再考虑。


会话超时:什么时候应该创建新的 visit_id

一个常见的问题:用户打开落地页后去做了别的事,30 分钟后回来继续填表,算一次访问还是两次访问?

这里有两种设计哲学:

Google Analytics 的方式:会话超时(默认 30 分钟无活动算会话结束)。超时后回来算新会话。

我们的推荐方式:每次落地页加载都是新的 visit_id,不考虑超时。

理由:

  1. 我们主要关心的是"广告点击 → 落地页加载 → 表单提交"这条链路。落地页加载就是流量入口,每次加载都对应一次广告曝光或搜索行为。
  2. 如果用户 30 分钟后回来,浏览器可能已经有新的 URL(直接访问而不是通过广告),这是一次新的"意向表达"。
  3. 简单性原则:每次加载 = 新的 visit_id,逻辑最清晰,最不容易出 bug。

第一方数据体系的四个核心属性

一个健康的第一方数据体系应该具备以下属性:

1. 完整性(Completeness) 每一次有效访问都应该被记录,不能有遗漏。这要求:前端脚本要足够轻量和可靠,不能因为广告拦截或网络问题导致大量记录失败。

2. 准确性(Accuracy) 记录下来的数据要准确。visitor_id 的生成和持久化机制要可靠,不能因为 Cookie 设置错误导致同一个用户生成了多个 visitor_id。

3. 关联性(Linkability) 访问记录(wb_visits)和转化记录(wb_leads)之间要能关联。这通过 visit_id 和 visitor_id 两个外键来实现。

4. 可操作性(Actionability) 数据收集的目的是支持决策。数据结构的设计要能直接回答以下问题:

  • 这个 lead 是哪个广告带来的?
  • A 版本和 B 版本哪个转化率更高?
  • 这个月花在 Facebook 上的预算产生了多少个 lead?

真实案例:一条数据记录的完整生命周期

让我们用一个真实的数据案例,把本章所有概念串联起来:

场景:用户 Lisa 点击了一个 Facebook 广告

1. 访问记录(wb_visits)
─────────────────────────────────────────────────────────────
visit_id:    "v-8f3a-92bc"    ← 本次访问唯一标识
visitor_id:  "vis-a1b2-c3d4"  ← Lisa 的持久化身份
qualifier:   "B"              ← 被分配到 B 版本落地页
landing_url: "https://site.com/insurance?utm_source=facebook"
referrer:    "https://www.facebook.com/"
utm_source:  "facebook"
utm_medium:  "cpc"
utm_campaign: "q1_homeowner_2024"
fbclid:      "AQH8f3a92bcXXXXXX"
created_at:  "2024-03-15 14:23:11"

2. Lisa 填写表单并提交(wb_leads)
─────────────────────────────────────────────────────────────
lead_id:     "lead-001"
name:        "Lisa Chen"
email:       "lisa@example.com"
phone:       "555-1234"
...其他业务字段...
visit_id:    "v-8f3a-92bc"    ← 关联到上面的访问记录
visitor_id:  "vis-a1b2-c3d4"  ← 同一个访客 ID
qualifier:   "B"              ← 她看的是 B 版本
wct_click_id: null            ← 这次不是 WCT 平台来的

3. 现在可以回答的问题
─────────────────────────────────────────────────────────────
Q: Lisa 从哪里来的?
A: Facebook 付费广告(utm_source=facebook, utm_medium=cpc)

Q: Lisa 看的是哪个版本的落地页?
A: B 版本(qualifier=B)

Q: Lisa 的 fbclid 是什么?(用于 Conversion 回传)
A: AQH8f3a92bcXXXXXX

Q: 这次转化归属于哪个广告系列?
A: q1_homeowner_2024

这就是第一方数据体系的价值:每一条 lead 记录背后,都有完整的来源信息,支持精准的归因和分析。


本章小结

  1. 身份识别是归因体系的基础:准确识别"同一个访客",才能追踪完整的转化旅程。

  2. 访客(Visitor)、会话(Visit)、事件(Event)是三个不同层级的概念,对应 visitor_id、visit_id 和具体的行为记录。

  3. visitor_id 需要通过 HTTP 响应头设置 Cookie,才能避免 Safari ITP 的 7 天有效期限制。

  4. visit_id 每次访问重新生成,是连接"广告来源"和"转化行为"的桥梁。

  5. 第一方数据体系需要具备完整性、准确性、关联性、可操作性四个属性。

核心行动建议:在设计你的追踪系统之前,先明确定义"访客"和"会话"的边界,这两个定义会影响后续所有的数据结构和业务分析。


→ 继续阅读:第03章 — UTM 参数工程,深入讲解流量来源标记的规范与最佳实践。