对于 Bilibili 抽奖的数据分析

8 minute read

最近开始深入数据分析的坑,虽然在学了 FIT1043,但这门课是第一年的课,加上比较水 (但又是必修课), 就没有学到东西,最近闲来无事,正好在哔哩哔哩参加了@毕导 THU的抽奖, 并且有 33 个名额,所以想着来做一次分析。

声明下,以下分析均为个人观点。并且数据仅供学习使用

前期准备

分析 API

首先,需要获取数据,在抽奖动态页面中进行抓包,可以看到一个对/lottery_notice的请求,查看内容可以看到所有中奖用户的 uid,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
    "code": 0,
    "data": {
        "first_prize": 3,
        "second_prize": 10,
        "third_prize": 20,
        "lottery_result": {
            "first_prize_result": [
                {
                    "uid": 21696250,
                    "name": "Alan 婪",
                    "face": "https://i1.hdslb.com/bfs/face/d03761481bdf0adec61e876a56309c1e2e444721.jpg"
                }
            ],
            "second_prize_result": [
                {
                    "uid": 17660688,
                    "name": "城西吖吖",
                    "face": "https://i1.hdslb.com/bfs/face/798c3f5751c934df20af1cb60b1ec7ba8e05f318.jpg"
                }
            ],
            "third_prize_result": [
                {
                    "uid": 4134700,
                    "name": "weacho",
                    "face": "https://i0.hdslb.com/bfs/face/9280e500dba7cf754c9e82e3428a3192de64e47c.jpg"
                }
            ]
        },
        "first_prize_cmt": "iPad Air 3 64G",
        "second_prize_cmt": "Kindle 青春版",
        "third_prize_cmt": "100 元现金红包",
    }
}

以及,打开用户动态,可以看到另一个请求/space_history,这个 API 返回了,用户的信息和发送、转发的动态。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
    "code": 0,
    "msg": "",
    "message": "",
    "data": {
        "has_more": 0,
        "attentions": {
            "uids": [
                ...
            ]
        },
        "cards": [
            {
                "desc": {
                    "uid": 21696250,
                    "type": 1,
                    "rid": 397377349256764900,
                    "acl": 0,
                    "view": 2552,
                    "repost": 2,
                    "comment": 21,
                    "like": 8,
                    "is_liked": 0,
                    "dynamic_id": 397377349254099260,
                    ...
            "card": "{ \"user\": { \"uid\": 21696250, \"uname\": \"Alan 婪\", \"face\": \"https:\\/\\/i1.hdslb.com\\/bfs\\/face\\/d03761481bd
            ...

接着获取转发抽奖动态所有人的信息

which 不太可能,一共有 18.7 万人转发,借口最多给 640 人,但我看了下数据,动态是 2020-05-27 18:02:12 发送的,第一个非@毕导 THU转发是 2020-05-27 18:07:38,最后一个转发 2020-06-06 13:36:59,所以可以当作 sample 使用 (么?)

但是,我发现评论接口没有限制,大概有 1.6 万人的评论,因为这是个抽奖动态,可以认定评论的人都参与了转发 (即抽奖),于是可以爬取评论,然后进行对比

数据清理

然后需要进行数据清理,通过获奖的 api,可以提取出所有用户的 uid,

1
2
3
4
5
6
7
8
9
list(
    map(
        lambda x: x["uid"], 
        reduce(
            lambda x, y: x + y, 
            data["data"]["lottery_result"].values()
        )
    )
)

以及将用户信息和发送、转发的动态提取出来

在这里,我只提取了我认为可能会有影响的因素

class Card 是转发和回复的内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class User(object):

    def __init__(self,
                 uid: int,
                 name: str,
                 level: int,
                 vip_type: int,
                 vip_status: int):
        self.uid = uid
        self.name = name
        self.level = level
        self.vip_type = vip_type
        self.vip_status = vip_status

class Card(object):

    def __init__(self,
                 content: str,
                 timestamp: int,
                 origin: Card = None,
                 id: int = None,
                 user: User = None):
        self.content = content
        self.timestamp = timestamp
        self.origin = origin
        self.id = id
        self.user = user

随后,将信息写入 csv 文件,以便之后使用 pandas 进行分析

分析

1
2
3
import pandas as pd
prize_users = pd.read_csv("./prize_users.csv")
all_users = pd.read_csv("./all_other_users.csv")

prize_users是中奖用户,all_users所有参与的用户,(因为接口问题,无法获取所有用户,同时评论有 1.6 万人,但因为怕被 ban ip 只先获取了 1.2 万人)

all_users 不包括发布抽奖的 up 主本人

数据定义

1
prize_users.head()
uidlevelvip_typevip_statustime_after
216962505001767
29332457510580531
940787035215347
1766068850063184
2241703651115013
  • uid,用户的 b 站 id
  • level,用户等级
  • time_after,在抽奖动态发送多少秒后转发的。

对于 vip_type 和 vip_status,通过openbilibili/app/admin/main/vip/model/vip.go中可以看到这项数值的定义。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// const vip enum value
const (
    ...

	NotVip    = 0 //非大会员
	Vip       = 1 //月度大会员
	AnnualVip = 2 //年度会员

	VipStatusOverTime    = 0 //过期
	VipStatusNotOverTime = 1 //未过期
	VipStatusFrozen      = 2 //冻结
	VipStatusBan         = 3 //封禁
    ...
)

prize_users 数据详情

uidlevelvip_typevip_statustime_after
count3.300000e+0133.00000033.00000033.00000033.000000
mean1.632111e+084.4848480.9090910.484848313364.333333
std1.724799e+080.8703880.8427500.507519320576.262967
min4.134700e+062.0000000.0000000.000000281.000000
25%2.525509e+074.0000000.0000000.0000003331.000000
50%4.962842e+075.0000001.0000000.000000199071.000000
75%2.941396e+085.0000002.0000001.000000597423.000000
max5.098047e+086.0000002.0000001.000000812534.000000

all_users 数据详情

uidlevelvip_typevip_statustime_after
count1.263000e+0412630.00000012630.00000012630.00000012630.000000
mean2.243269e+083.8292950.5422800.236817250477.374822
std1.743140e+080.9990870.7265710.425146289099.261204
min1.300000e+011.0000000.0000000.000000212.000000
25%3.654792e+073.0000000.0000000.00000010677.750000
50%2.382717e+084.0000000.0000000.00000059733.500000
75%3.855455e+085.0000001.0000000.000000583955.000000
max6.022326e+086.0000002.0000001.000000852418.000000

数据分析

用户等级分布

用户等级分布

左边的是得奖用户的等级划分,右边则是所有用户的等级。

得奖用户中的 5 级账号是明显多余其他等级的,占总共的 50% 以上。

并且中奖用户的平均等级是 4.48,而所有用户等级的平均数是在 3.82, (std 相差 .129),即中奖用户等级偏高。

大会员占比

大会员状态

大会员类型

可以看到得奖用户的大会员状态占比基本持平,但可以看到没有大会员的用户是有大会员用户数的 3 倍,即大会员会对抽奖结果造成影响 (特殊加成),但是,年度大会员和月度大会员似乎没有特殊加成

转发时间

转发时间

X轴是以小时为单位的,即转发时间为抽奖动态发布的 x 小时

中奖用户转发的时间和大部分用户发帖的时间基本一致,

前 5 个小时转发

但在这前 5 个小时转发的图里,可以看到有 11 个 (33%) 中奖用户的转发时间是在前 2 个小时,似乎在抽奖动态发出后半个小时和一个小时间转发的中奖率更大。

结论

怀疑(不用怀疑) 数据有缺失,导致结果不准

就如我文章前头说的,都是个人观点,因为只是学习使用,分析啥的都很渣。

但通过这些数据,得出的结论是,大会员会有特殊加成,前 2 个小时转发也会有加成 (大概).