对于 Bilibili 抽奖的数据分析

最近开始深入数据分析的坑, 虽然在学了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
28
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个小时转发也会有加成(大概).