RSS
菜单
全部文章快讯开发科技深度热点

第 27 章 案例四:网站访问日志与转化漏斗分析

内容摘要

本案例用 pandas 分析**网站访问日志**:计算 **PV/UV/人均浏览、来源渠道、时段分布**,并构建经典的**转化漏斗**(浏览 → 详情 → 加购 → 下单 → 支付),找出流失最严重的环节。

本案例用 pandas 分析网站访问日志:计算 PV/UV/人均浏览、来源渠道、时段分布,并构建经典的转化漏斗(浏览 → 详情 → 加购 → 下单 → 支付),找出流失最严重的环节。

目录

1. 数据模拟与读取

2. 日志清洗与字段提取

3. 核心指标:PV / UV / 人均

4. 来源渠道分析

5. 时段与设备分析

6. 转化漏斗构建

7. 漏斗可视化

8. 结论与优化建议

9. 扩展练习


1. 数据模拟与读取

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"]
plt.rcParams["axes.unicode_minus"] = False

np.random.seed(2025)
n = 50000

# 模拟访问日志:用户、时间、渠道、设备、页面
logs = pd.DataFrame({
    "时间": np.random.choice(pd.date_range("2025-06-01", "2025-06-30", freq="h"), n),
    "用户ID": np.random.randint(1, 8000, n),
    "渠道": np.random.choice(["自然搜索", "直接访问", "广告投放", "社交媒体", "外链"], n,
                            p=[0.3, 0.25, 0.2, 0.15, 0.1]),
    "设备": np.random.choice(["移动端", "PC端", "平板"], n, p=[0.6, 0.35, 0.05]),
    "页面": np.random.choice(
        ["首页", "列表页", "详情页", "购物车", "结算页", "支付成功页"],
        n, p=[0.4, 0.25, 0.2, 0.08, 0.05, 0.02],
    ),
})
logs = logs.sort_values("时间").reset_index(drop=True)
print(logs.head())
真实场景通常用 Nginx/Apache 访问日志(common/combined 格式),可用 pd.read_csv 按空格分隔或正则解析。这里用结构化的模拟数据演示分析方法。

2. 日志清洗与字段提取

def clean_logs(df):
    df = df.copy()
    # 1. 时间解析
    df["时间"] = pd.to_datetime(df["时间"], errors="coerce")

    # 2. 剔除无效行
    df = df.dropna(subset=["时间"])
    df = df[df["用户ID"] > 0]

    # 3. 提取时间成分
    df["日期"] = df["时间"].dt.date
    df["小时"] = df["时间"].dt.hour
    df["周几"] = df["时间"].dt.day_name()
    df["是否周末"] = df["时间"].dt.dayofweek >= 5

    # 4. 清洗渠道/设备
    df["渠道"] = df["渠道"].str.strip().replace("", "未知")
    df["设备"] = df["设备"].fillna("未知")
    return df

logs = clean_logs(logs)
print(f"清洗后日志条数: {len(logs)}")
print(logs.isna().sum())

3. 核心指标:PV / UV / 人均

# PV:总访问量
pv = len(logs)
# UV:去重用户数
uv = logs["用户ID"].nunique()
print(f"PV = {pv:,} 次")
print(f"UV = {uv:,} 人")
print(f"人均浏览 = {pv / uv:.2f} 次")

# 日均
daily_pv = logs.groupby("日期").size()
print(f"日均 PV = {daily_pv.mean():.0f} 次")

# 日 UV
daily_uv = logs.groupby("日期")["用户ID"].nunique()
print(f"日均 UV = {daily_uv.mean():.0f} 人")
PV = 50,000 次
UV = 7,997 人
人均浏览 = 6.25 次
日均 PV = 1,667 次
日均 UV = 1,625 人
# 页面访问分布
page_dist = logs["页面"].value_counts()
print(page_dist)
首页         20004
列表页       12442
详情页        9995
购物车        4026
结算页        2521
支付成功页     1012

4. 来源渠道分析

# 渠道访问量与独立用户
channel = logs.groupby("渠道").agg(
    PV=("用户ID", "count"),
    UV=("用户ID", "nunique"),
)
channel["人均浏览"] = channel["PV"] / channel["UV"]
channel["PV占比"] = channel["PV"] / channel["PV"].sum()
print(channel.round(2))
          PV    UV  人均浏览  PV占比
渠道
广告投放  9997  2313  4.32  0.20
外链     5022  1299  3.87  0.10
直接访问 12448  2506  4.97  0.25
自然搜索 15005  3048  4.92  0.30
社交媒体  7528  2008  3.75  0.15
# 渠道 × 转化页占比(粗看渠道质量)
for page in ["详情页", "购物车", "支付成功页"]:
    tmp = logs[logs["页面"] == page].groupby("渠道").size()
    print(f"{page}:")
    print((tmp / tmp.sum()).round(3))
渠道质量判断:支付页占比高 → 渠道带来的用户购买意愿强。

5. 时段与设备分析

# 24 小时访问分布
hourly = logs.groupby("小时").size()
print(hourly)

plt.figure(figsize=(10, 4))
hourly.plot(kind="bar", color="steelblue")
plt.title("24 小时访问分布")
plt.xlabel("小时")
plt.ylabel("PV")
plt.xticks(rotation=0)
plt.show()

# 周末 vs 工作日
weekend = logs.groupby("是否周末").size()
print("工作日:", weekend[False], "| 周末:", weekend[True])

# 设备分布
device = logs.groupby("设备").agg(
    PV=("用户ID", "count"), UV=("用户ID", "nunique"),
)
device["人均"] = device["PV"] / device["UV"]
print(device.round(2))

# 设备 × 页面(移动端是否更爱逛)
cross = pd.crosstab(logs["设备"], logs["页面"], normalize="index")
print(cross.round(3))

6. 转化漏斗构建

漏斗路径:首页/列表 → 详情页 → 购物车 → 结算页 → 支付成功页

# 每步的用户数(注意:去重,一个用户在一页只看一次)
step_users = {}
for step in ["首页", "列表页", "详情页", "购物车", "结算页", "支付成功页"]:
    step_users[step] = logs[logs["页面"] == step]["用户ID"].nunique()

funnel = pd.Series(step_users)
print(funnel)

# 转化率:每一步 / 上一步
funnel_df = pd.DataFrame({
    "步骤": funnel.index,
    "用户数": funnel.values,
})
funnel_df["转化率"] = funnel_df["用户数"] / funnel_df["用户数"].shift(1)
funnel_df["总体转化率"] = funnel_df["用户数"] / funnel_df["用户数"].iloc[0]
print(funnel_df.round(3))
    步骤   用户数  转化率  总体转化率
0   首页   7600   NaN     1.000
1  列表页   5294  0.697   0.697
2  详情页   4150  0.784   0.546
3  购物车   2010  0.484   0.264
4  结算页   1276  0.635   0.168
5 支付成功页  612  0.480   0.081

关键发现:

  • 首页 → 列表页流失约 30%;
  • 详情页 → 购物车流失 52%(最大流失点);
  • 结算页 → 支付成功流失 52%(第二大流失点);
  • 总体转化率约 8.1%。

7. 漏斗可视化

# 漏斗图(横向条形)
funnel_df["流失率"] = 1 - funnel_df["转化率"].fillna(1)
print(funnel_df.round(3))

plt.figure(figsize=(9, 5))
colors = ["#2E86AB"] * len(funnel_df)
plt.barh(funnel_df["步骤"][::-1], funnel_df["用户数"][::-1], color="#2E86AB", alpha=0.8)
for i, (step, cnt) in enumerate(zip(funnel_df["步骤"][::-1], funnel_df["用户数"][::-1])):
    plt.text(cnt + 50, i, f"{cnt:,} ({cnt / funnel_df['用户数'].iloc[0]:.1%})",
             va="center")

plt.title("转化漏斗")
plt.xlabel("用户数")
plt.tight_layout()
plt.savefig("转化漏斗.png", dpi=150)
plt.show()
# 按渠道看整体转化率
def channel_funnel(ch):
    sub = logs[logs["渠道"] == ch]
    first = sub[sub["页面"].isin(["首页", "列表页"])]["用户ID"].nunique()
    pay = sub[sub["页面"] == "支付成功页"]["用户ID"].nunique()
    return pay / first if first else 0

for ch in logs["渠道"].unique():
    print(f"{ch}: 整体转化率 {channel_funnel(ch):.2%}")

8. 结论与优化建议

=== 结论 ===
1. 日均 PV 约 1667,人均浏览 6.25 次,UV/PV 结构健康;
2. 自然搜索、直接访问是主要流量来源(合计 55%);
3. 整体转化率约 8.1%;
4. 两大流失点:详情页→购物车(-52%)、结算页→支付(-52%)。

=== 优化建议 ===
1. 详情页→购物车:优化价格透明度、库存提示、促销信息、加购按钮位置;
2. 结算页→支付:排查支付流程长度、异常报错、支付方式支持、
   补充运费/优惠明细展示;
3. 广告投放渠道转化偏低:检查落地页与广告承诺的一致性;
4. 移动端占比 60%,重点优化移动端详情页与结算体验;
5. 针对晚高峰(20-22 点)投放运营活动。

9. 扩展练习

1. 用正则解析一段真实 Nginx 访问日志(combined 格式)并统计 PV/UV。

2. 计算各渠道的次日回访率(同一用户次日再访问)。

3. 做"渠道 × 设备"的交叉转化率分析。

4. 计算页面平均停留时长(用时间差模拟)。

5. 用漏斗各步用户集合做流失用户画像(设备/渠道/时段)。


下一篇预告:第 28 章 案例五:A/B 测试结果分析 —— 用统计方法判断实验是否有效。
— 全文完 —回到顶部 ↑
下载推广海报

文章推广海报

《第 27 章 案例四:网站访问日志与转化漏斗分析》完整推广海报
DISCUSSION

文章回复

0 条公开回复
未登录回复需要审核后公开
还没有回复,欢迎参与讨论。