2022年2月17日

获取 B 站加权评分的番剧排行榜

摘  要

B 站上番剧排行榜都是按照单一指标(如追番数,观看数,用户评分)进行的,这无法较好的反映番剧的评分,这里通过 http 接口获取番剧的信息然后加权评分得出排行榜。
关键词: 动漫 番剧 排行榜
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"math"
	"net/http"
	"sort"
	"strconv"
	"strings"
)

const urlTemplate = "https://api.bilibili.com/pgc/season/index/result?season_version=-1&spoken_language_type=-1&area=-1&is_finish=-1&copyright=-1&season_status=-1&season_month=-1&year=-1&style_id=-1&order=%d&st=1&sort=0&page=1&season_type=1&pagesize=%d&type=1"

const (
	byScore     = 4
	byFavorites = 3
	byPlay      = 2
)

type Media struct {
	Id        int
	Title     string
	Score     float64
	Favorites float64
	Play      float64
	Season    int

	Weight float64
}

func (m *Media) calculateWeightedScore(maxFavorites, maxPlay float64) {
	const scoreWeight = 0.8
	const favoritesWeight = 0.1
	const playWeight = 0.1
	m.Weight = scoreWeight*m.Score +
		favoritesWeight*10*normalize(m.Favorites/maxFavorites) +
		playWeight*10*normalize(m.Play/maxPlay)
}

func normalize(x float64) float64 {
	return math.Sqrt(x)
}

func log10(x int) int {
	var n int
	for x >= 10 {
		x /= 10
		n++
	}
	return n
}

func prettyNumber(x int) string {
	var format string
	var value interface{}
	var pow = log10(x)
	if pow < 4 {
		return strconv.Itoa(x)
	} else if pow < 8 {
		if pow == 7 {
			format = "%d万"
			value = x / 1e4
		} else {
			format = fmt.Sprintf("%%.%df万", 7-pow)
			value = float64(x) / 1e4
		}
	} else if pow < 10 {
		format = fmt.Sprintf("%%.%df亿", 10-pow)
		value = float64(x) / 1e8
	} else {
		format = "%d亿"
		value = x / 1e8
	}
	return fmt.Sprintf(format, value)
}

func isDigit(b byte) bool {
	return b >= '0' && b <= '9'
}

func trim(s string) string {
	const n = 64
	var v []rune
	for i, r := range s {
		if i >= n {
			v = append(v, '.', '.', '.')
			break
		}
		v = append(v, r)
	}
	return string(v)
}

func parseFloatPrefix(s string) float64 {
	var n = len(s)
	for i := 0; i < n; i++ {
		if !isDigit(s[i]) && s[i] != '.' {
			n = i
			break
		}
	}
	if n == 0 {
		return 0
	}
	var unit = float64(1)
	if strings.HasPrefix(s[n:], "十亿") {
		unit = 1e9
	} else if strings.HasPrefix(s[n:], "亿") {
		unit = 1e8
	} else if strings.HasPrefix(s[n:], "千万") {
		unit = 1e7
	} else if strings.HasPrefix(s[n:], "百万") {
		unit = 1e6
	} else if strings.HasPrefix(s[n:], "十万") {
		unit = 1e5
	} else if strings.HasPrefix(s[n:], "万") {
		unit = 1e4
	}
	v, _ := strconv.ParseFloat(s[:n], 64)
	return v * unit
}

func query(medias []Media, indices map[int]int, by int, size int) []Media {
	var result struct {
		Code int `json:"code"`
		Data struct {
			List []struct {
				MediaId   int    `json:"media_id"`
				Title     string `json:"title"`
				OrderType string `json:"order_type"`
				Order     string `json:"order"`
				Season    int    `json:"season_type"`
			} `json:"list"`
		} `json:"data"`
	}
	var url = fmt.Sprintf(urlTemplate, by, size)
	var resp, err = http.Get(url)
	if err != nil {
		log.Fatalf("get by %d error: %v", by, err)
	}
	defer resp.Body.Close()
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		log.Fatalf("unmarshal from json error: %v", err)
	}
	for i := range result.Data.List {
		index, ok := indices[result.Data.List[i].MediaId]
		if !ok {
			index = len(medias)
			var m = Media{
				Id:     result.Data.List[i].MediaId,
				Title:  result.Data.List[i].Title,
				Season: result.Data.List[i].Season,
			}
			indices[m.Id] = index
			medias = append(medias, m)
		}
		order := parseFloatPrefix(result.Data.List[i].Order)
		switch by {
		case byScore:
			medias[index].Score = order
		case byFavorites:
			medias[index].Favorites = order
		case byPlay:
			medias[index].Play = order
		default:
			panic("unknown orderby")
		}
	}

	return medias
}

func main() {
	var medias []Media
	var indices = make(map[int]int)
	const size = 4000
	medias = query(medias, indices, byScore, size)
	medias = query(medias, indices, byFavorites, size)
	medias = query(medias, indices, byPlay, size)
	var maxFavorites, maxPlay float64
	for i, m := range medias {
		if i == 0 || m.Favorites > maxFavorites {
			maxFavorites = m.Favorites
		}
		if i == 0 || m.Play > maxPlay {
			maxPlay = m.Play
		}
	}
	for i := range medias {
		medias[i].calculateWeightedScore(maxFavorites, maxPlay)
	}
	sort.Slice(medias, func(i, j int) bool {
		return medias[i].Weight > medias[j].Weight
	})

	var table bytes.Buffer
	var maxCount = 200
	table.WriteString(`
|排名|番名|用户评分|追番数|播放次数|权重评分|
|----|----|--------|------|--------|--------|`)
	for i, m := range medias {
		if i >= maxCount {
			break
		}
		fmt.Fprintf(&table, "\n|%d|%s|%.01f|%s|%s|%.01f|", i+1, trim(m.Title), m.Score, prettyNumber(int(m.Favorites)), prettyNumber(int(m.Play)), m.Weight)
	}
	fmt.Println(table.String())
}

根据上面的代码获取 Top-200 的番剧如下:

排名番名用户评分追番数播放次数权重评分
1鬼灭之刃9.71144万8.10亿9.7
2咒术回战9.71069万6.70亿9.6
3鬼灭之刃 无限列车篇9.81276万1.20亿9.2
4名侦探柯南9.8393.9万5.10亿9.2
5JOJO的奇妙冒险 黄金之风9.8468.6万4.40亿9.2
6小林家的龙女仆9.7774.8万3.20亿9.2
7辉夜大小姐想让我告白~天才们的恋爱头脑战~9.8732.6万2.50亿9.2
8辉夜大小姐想让我告白?~天才们的恋爱头脑战~9.8745.5万2.00亿9.1
9青春猪头少年不会梦到兔女郎学姐9.8732.8万2.00亿9.1
10Re:从零开始的异世界生活 第二季 后半9.9844.2万8797万9.1
11OVERLORD9.6600.2万3.90亿9.1
12堀与宫村9.8653.0万2.00亿9.1
13某科学的超电磁炮T9.8620.1万2.00亿9.0
14紫罗兰永恒花园9.8741.2万1.50亿9.0
15JOJO的奇妙冒险 石之海9.9733.6万1.00亿9.0
16工作细胞9.6796.3万2.20亿9.0
17OVERLORD Ⅱ9.6527.0万3.60亿9.0
18关于我转生变成史莱姆这档事9.4722.7万4.10亿9.0
19冰菓9.8673.3万1.20亿9.0
20Re:从零开始的异世界生活 第二季 前半9.7769.7万1.30亿8.9
21JOJO的奇妙冒险9.8530.0万1.60亿8.9
22四月是你的谎言9.8518.6万1.50亿8.9
23关于我转生变成史莱姆这档事 转生史莱姆日记9.7829.2万9037万8.9
24碧蓝之海9.9417.8万1.30亿8.9
25猫和老鼠 旧版9.9261.2万2.10亿8.9
26名侦探柯南(中配)9.8112.1万4.30亿8.9
27工作细胞 第二季9.7836.2万6981万8.9
28OVERLORD Ⅲ9.4539.1万3.80亿8.9
29犬夜叉9.9216.6万2.20亿8.9
30徒然喜欢你9.7549.9万1.50亿8.8
31这个勇者明明超强却过分慎重9.7503.2万1.70亿8.8
32紫罗兰永恒花园 剧场版9.9787.0万1503万8.8
33银魂9.9242.2万1.90亿8.8
34异度侵入 ID:INVADED9.8464.0万1.20亿8.8
35JOJO的奇妙冒险 星尘远征军9.8430.1万1.30亿8.8
36某科学的超电磁炮9.7519.9万1.40亿8.8
37紫罗兰永恒花园外传:永远与自动手记人偶9.9594.8万3439万8.8
38JOJO的奇妙冒险 不灭钻石9.8288.5万1.90亿8.8
39JOJO的奇妙冒险 星尘远征军 埃及篇9.8410.1万1.20亿8.8
40我的青春恋爱物语果然有问题。续9.8320.6万1.40亿8.8
41小林家的龙女仆 第二季9.5847.9万9083万8.8
42NO GAME NO LIFE 游戏人生9.8447.4万8075万8.7
43路人超能100 II(灵能百分百 第二季)9.9279.3万9861万8.7
44岸边露伴 一动也不动9.9510.3万2310万8.7
45夏目友人帐 唤石者与怪异的访客9.9566.7万1260万8.7
46路人超能100(灵能百分百)9.8324.2万1.00亿8.7
47刀剑神域9.7337.7万1.40亿8.7
48日常9.9344.2万5086万8.7
49埃罗芒阿老师9.3700.0万2.10亿8.7
50致不灭的你9.6468.6万1.30亿8.7
51卫宫家今天的饭9.8351.0万8247万8.7
52Dr.STONE 石纪元9.7328.1万1.40亿8.7
53你的名字。9.9270.2万7151万8.7
54天使降临到我身边9.8362.2万7473万8.7
55Re:从零开始的异世界生活 新编集版9.6620.1万7152万8.7
56中二病也要谈恋爱!9.8452.8万4387万8.7
57我的青春恋爱物语果然有问题。9.7430.8万8635万8.7
58擅长捉弄的高木同学 第二季9.9273.4万6381万8.7
59黑色四叶草9.5264.1万3.00亿8.7
60约定的梦幻岛9.8343.7万7495万8.7
61CLANNAD9.9334.3万4268万8.7
62齐木楠雄的灾难9.7297.4万1.40亿8.7
63齐木楠雄的灾难 第二季9.8238.6万1.20亿8.7
64Fate/stay night [Unlimited Blade Works] 第一季9.5437.1万1.80亿8.7
65月色真美9.8355.1万6743万8.7
66命运-冠位指定 绝对魔兽战线 巴比伦尼亚9.5476.5万1.60亿8.7
67异世界迷宫黑心企业9.8317.7万8034万8.7
68迪迦奥特曼(中配)9.9167.0万1.00亿8.6
69青春猪头少年不会梦到怀梦美少女9.8494.2万2351万8.6
70总之就是非常可爱9.5485.7万1.40亿8.6
71五等分的新娘∬9.7452.4万6202万8.6
72游戏人生 零9.9352.0万2585万8.6
73Fate/Zero 第一季9.6397.0万1.20亿8.6
74家庭教师HITMAN REBORN!9.9164.6万9547万8.6
75关于我转生变成史莱姆这档事 第二季8.9889.6万3.60亿8.6
76在下坂本,有何贵干?9.3570.1万2.10亿8.6
77女高中生的虚度日常9.8309.1万6535万8.6
78月刊少女野崎君9.8376.1万4404万8.6
79Angels of Death9.5504.1万1.20亿8.6
80工作细胞BLACK9.8292.5万7018万8.6
81魔卡少女樱 CLEAR CARD篇9.8293.9万6669万8.6
82文豪野犬 第三季9.8289.3万6392万8.6
83动物狂想曲 / BEASTARS9.7341.7万8142万8.6
84齐木楠雄的灾难(日播&精选版)9.6184.4万2.30亿8.6
85入间同学入魔了 第二季9.7335.2万8266万8.6
86擅长捉弄的高木同学 第三季9.9314.5万2352万8.6
87男子高中生的日常9.8291.5万5665万8.6
88多罗罗9.4445.3万1.80亿8.6
89关于前辈很烦人的事9.8269.7万6335万8.6
90Dr.STONE 石纪元 (第二季)9.7368.7万6259万8.6
91魔女之旅9.7332.1万7451万8.6
92夏目友人帐9.8312.3万4605万8.6
93齐木楠雄的灾难 始动篇9.8370.9万2896万8.6
94轻音少女 第一季9.9233.9万3771万8.6
95Charlotte9.4476.4万1.50亿8.6
96可塑性记忆9.7327.6万6976万8.6
97会长是女仆大人!9.8249.3万6200万8.6
98宝石之国9.7325.3万6946万8.6
99樱花庄的宠物女孩9.7371.6万5200万8.6
100装甲重拳/MEGALOBOX 第二季9.9240.6万3179万8.6
101干物妹!小埋R9.4479.2万1.40亿8.5
102命运-冠位嘉年华9.8481.0万687.5万8.5
103百变小樱9.8185.7万8123万8.5
104某科学的超电磁炮S9.7261.4万8512万8.5
105路人女主的养成方法 Fine9.8412.3万1305万8.5
106龙与虎9.7320.7万5903万8.5
107银魂 第二季9.9157.1万5227万8.5
108强风吹拂9.9174.4万4453万8.5
109装甲重拳/MEGALOBOX9.8215.6万5907万8.5
110蜡笔小新 第二季(中文)9.859.00万1.70亿8.5
111玉子市场9.8265.4万3799万8.5
112Re:从零开始的异世界生活 Memory Snow(雪之回忆)9.7491.7万1368万8.5
113火影忍者 疾风传9.6190.4万1.60亿8.5
114干物妹!小埋9.3477.9万1.70亿8.5
115珈百璃的堕落9.6321.2万8583万8.5
116Fate/stay night [Unlimited Blade Works] 第二季9.5290.1万1.50亿8.5
117路人女主的养成方法9.4415.8万1.40亿8.5
118食戟之灵9.5253.4万1.70亿8.5
119在魔王城说晚安9.7276.6万6242万8.5
120这个美术社大有问题!9.8220.3万4935万8.5
121蜡笔小新 第一季(中文)9.893.80万1.20亿8.5
122命运之夜——天之杯II :迷失之蝶9.7416.3万2184万8.5
123夏目友人帐 第六季9.8215.4万4799万8.5
124请问您今天要来点兔子吗?BLOOM9.9249.0万1399万8.5
125我们仍未知道那天所看见的花的名字。9.6412.6万4805万8.5
126Fate/Zero 第二季9.6230.9万1.20亿8.5
127转生成为了只有乙女游戏破灭Flag的邪恶大小姐 第…9.6375.0万5806万8.5
128极主夫道9.7287.1万5259万8.5
129理科生坠入情网,故尝试证明。9.7292.1万5084万8.5
130超能力女儿9.6285.4万8898万8.5
131戒律的复活9.7197.7万8818万8.5
132玉子爱情故事9.9213.8万1878万8.5
133ReLIFE9.9192.2万2371万8.5
134少女终末旅行9.8233.6万3595万8.5
135银魂 第三季9.9154.4万3512万8.5
136打工吧!魔王大人9.6249.9万1.00亿8.5
137CAROLE & TUESDAY9.7232.8万6599万8.5
138此花亭奇谭9.8228.6万3462万8.5
139路人女主的养成方法 ♭9.7360.7万2555万8.5
140阿宅的恋爱真难9.6313.2万6969万8.5
141欢迎光临樱兰高校9.9133.1万4121万8.5
142夏目友人帐 第五季9.7260.5万5328万8.5
143入间同学入魔了9.5277.7万1.30亿8.5
144文豪野犬 汪!9.8279.7万2031万8.5
145鬼灭之刃 中配版9.1769.8万1.30亿8.5
146转生成为了只有乙女游戏破灭Flag的邪恶大小姐9.5330.6万9674万8.5
147妖精森林的小不点9.9155.0万2772万8.5
148魔法少女小圆9.8204.5万3659万8.5
149EVA 新世纪福音战士9.7208.4万6674万8.5
150甘城光辉游乐园9.7273.2万4128万8.4
151元气少女缘结神◎9.7268.4万4207万8.4
152游戏王 怪兽之决斗9.883.00万1.00亿8.4
153CLANNAD ~AFTER STORY~9.9145.9万2816万8.4
154悠哉日常大王 第三季9.9171.1万2006万8.4
155君主·埃尔梅罗二世事件簿 魔眼收集列车 Grace not…9.7236.8万5078万8.4
156奇蛋物语 / WONDER EGG PRIORITY9.7246.6万4492万8.4
157幻界战线 & BEYOND9.8177.9万3974万8.4
158约战狂三外传9.6450.3万2064万8.4
159冰海战记9.8162.7万4505万8.4
160俺物语!!9.9130.3万2750万8.4
161公主连结Re:Dive9.5307.9万8953万8.4
162街角魔族9.8176.6万3623万8.4
163轻音少女 第二季9.9109.2万3603万8.4
164死神少爷与黑女仆9.7236.0万4297万8.4
165路人超能100 第一次灵能咨询所员工旅游~舒缓心…9.8318.6万478.4万8.4
166黑执事9.8181.9万3182万8.4
167佐贺偶像是传奇 卷土重来9.9134.5万2326万8.4
168国王排名8.6936.6万3.70亿8.4
169赛马娘 第二季9.9131.6万2230万8.4
170迪迦奥特曼9.9106.4万3115万8.4
171因为太怕痛就全点防御力了9.0595.4万2.20亿8.4
172One Room 第三季9.8204.4万2077万8.4
173SSSS.电光机王9.8164.6万3276万8.4
174终将成为你9.8184.0万2611万8.4
175幻界战线9.6240.9万6496万8.4
176邪神与厨二病少女 第二季9.8170.9万2887万8.4
177排球少年!!9.994.40万3251万8.4
178天气之子9.6314.7万3586万8.4
179邻家索菲9.8161.8万2888万8.4
180薇薇 -萤石眼之歌-9.8160.4万2927万8.4
181境界的彼方9.5383.4万4510万8.4
182吹响吧!上低音号9.8147.3万3353万8.4
183小木乃伊到我家9.6244.5万5585万8.4
184言叶之庭9.7255.8万2408万8.4
185打了300年的史莱姆,不知不觉就练到了满级9.5274.4万8022万8.4
186命运-冠位指定 -月光/失落之室-9.6486.0万461.5万8.4
187世界顶尖的暗杀者,转生为异世界贵族9.2470.0万1.30亿8.4
188我们的重制人生9.6230.7万5447万8.4
189泽塔奥特曼9.896.40万4880万8.4
190野良神9.5320.8万5424万8.4
191关于完全听不懂老公在说什么的事 第一季9.7127.2万6526万8.4
192灰与幻想的格林姆迦尔9.8163.4万2111万8.4
193流汗吧!健身少女9.7170.1万4216万8.4
194比宇宙更远的地方9.8152.8万2231万8.4
195Re:从零开始的异世界生活 冰结之绊9.5491.2万1387万8.4
196虫师9.9118.2万1299万8.4
197斩服少女9.8162.4万1878万8.3
198野良神 ARAGOTO9.4251.1万1.20亿8.3
199Angel Beats!9.7234.5万2055万8.3
200×××HOLiC9.8156.7万1993万8.3