歡迎光臨
每天分享高質量文章

爬了31574條游戲評分資料,找出了最好玩的84款,你玩過嗎?

導讀:本文爬取了豆瓣游戲網站上所有可見的游戲評分資料進行分析,全文包括以下幾個部分:

  1. 資料獲取

  2. 資料總覽

  3. 游戲型別分析

  4. 游戲平臺分析

  5. 游戲名稱分析

  6. 高分游戲彙總

  7. 代碼彙總

對代碼不感興趣的可以直接跳過第一部分。需要代碼的同學,請註意本文也末尾,已把全部用到的代碼彙總到文章末尾,供參考。

全文資料獲取及分析均基於python3.6完成。

 

 

作者:量化小白H

來源:量化小白上分記(ID:quanthzp)

 

 

01 資料獲取過程

資料來自豆瓣游戲,網址:

https://www.douban.com/game/explore

頁面內一條游戲資料展示如下,顯示出來的一條評論是游戲的點贊數最多的評論,我們分析需要的資料包括游戲名稱游戲型別游戲平臺游戲評分游戲評價人數及最熱評價

瀏覽器中按F12打開開發者工具,選擇NetWork-XHR,頁面拉倒底部點顯示更多,可以看到獲取到的資料檔案。

右鍵打開後看到是游戲的信息,通過改變網址中more後面的數字,可以獲取更多資料。但嘗試之後發現,每次可以獲取20條資料,more後面的數字最大可以設置為500,超過500後獲取不到資料,也就是說最多能獲取10000條資料,但底部total欄位顯示總的游戲資料有52049條。

所以為了獲取更多資料,我們分型別爬取資料,每次選中一個型別,重覆上述過程,可以得到資料,觀察後發現每個型別下的游戲資料都不超過10000條,這樣每個型別的資料都能全部獲取,最後把所有資料拼到一起即可。

以動作類游戲為例,動作類游戲第二頁資料對應的網址如下

https://www.douban.com/j/ilmen/game/search?genres=1&platforms=&q;=&sort=rating&more=2

多嘗試幾次之後能看出規律:genres後面是游戲型別,動作型別對應的genres = 1,platforms後面是平臺型別,q後面的是游戲名稱關鍵字,sort後面是排序方式,預設是按評分排序,more後面是頁碼。

所以我們需要知道每個游戲型別對應的數字,可以在開發者工具中選Element,用小箭頭進行定位,快速獲取所有游戲型別對應的數字:

定位後發現,每種型別包含在一個class內,動作型別對應的數字在values里。

依次點開每個class,獲取每個型別對應的數字,整理如下:

{1:”動作”,5:”角色扮演”,41:”橫版過關”,4:”冒險”,48:”射擊”,32:”第一人稱射擊”,2:”策略”,18:”益智”,7:”模擬”,3:”體育”,6:”競速”,9:”格鬥”,37:”亂鬥/清版”,12:”即時戰略”,19:”音樂/旋律”}

之後就可以用python中的request+json包迴圈獲取資料了,代碼附在最後。爬到的一條游戲資料樣式如下:

需要的資料包括:

  • n_ratings:評分人數

  • platforms:平臺

  • rating:評分

  • star:星級

  • title:游戲名稱

  • content:最熱評論

游戲型別因為已經我們已經分型別爬取,所以每次爬完之後用代碼加上對應的型別即可,但能看到一個游戲可能對應多種型別,或者在多個平臺上同時發佈,所以在後面的分析中需要處理,其他欄位分析中用不到。

 

 

02 資料總覽

 

最終爬下來資料有31574條,還是沒拿到所有5萬條,這已經是最大可見資料條數了,資料樣式如下:

genres是網站上給的型別,type是爬取過程中加上去的型別,後文的分析都用type。

簡單統計後發現,31574條資料中有17751條資料都是沒有評分的,此外,由於一種游戲可能屬於多個類別,所以一部分游戲是重覆出現的,只有type不同。

首先對資料整體做一個統計描述,對於沒有評分的資料採用兩種方法處理,視為評分為0和刪除資料。

視為評分為0時:

刪除沒有評分的資料:

兩幅圖的5個變數(從左往右、從上至下)均為:星級、評分、評分人數、游戲名稱長度、游戲發行平臺數,加入游戲名稱長度和發行平臺數是想探究游戲名稱的長度以及發行的平臺數是否和游戲評分有一定的關係。

從這兩幅分佈圖能得到一些結論:

  1. 星級(star)和評分(rating)是嚴格單調關係的,星級是分級靠檔後的評分,所以之後的分析中只考慮星級不考慮評分。

  2. 兩種評分處理方法下,各變數的分佈基本不變,大部分游戲評分集中在7.5左右,評分跟發行平臺數、游戲名稱長度關係不是非常明顯。

 

 

03 游戲型別分析

1. 各型別游戲數

豆瓣給出的游戲共有15類,各型別游戲數統計如下:

動作、冒險、角色扮演、益智、策略類的游戲數量排到前5,第一人稱射擊、即時戰略、音樂、亂鬥/清版、射擊類排到後5。

豆瓣把第一人稱射擊、射擊分成兩類,亂鬥跟冒險、動作的區別似乎不是很大?這裡不是很理解。不過射擊類的游戲樣式太過單一,數量確實比較少,高質量的射擊游戲不外乎這兩年火起來的吃雞游戲,還有很早之前的CS系列。

而動作、冒險、角色扮演類的游戲從世界觀設定、劇情設計上都可以有很多新意,時不時會有一些讓人眼前一亮的新作品,也很容易做成一個系列。

2. 各型別游戲平均評分人數

評分人數最多的是第一人稱射擊類游戲,冒險、即時戰略類也較多。

3. 各型別游戲均分

各型別游戲的均分如下,無評分視為0時,由於各種游戲數量的差別,導致游戲數較少的型別平均分更高,但刪掉無評分資料後,各種型別的評分基本是持平的,在7.5分上下波動。

總體來看,各種型別游戲的平均質量基本是一樣的,只是各種型別的用戶基數差別大一些。

4. 游戲型別關聯分析

之前提到,一個游戲可能屬於多個類別,比如仙劍同時屬於角色扮演和冒險。對所有的游戲類別進行交叉分析,統計同時屬於兩個類別的游戲個數,結果如下:

可以看到,動作+冒險,角色扮演+動作/冒險、橫版過關+動作/冒險、益智+冒險 組合的游戲都是非常多的,動作、冒險大概是萬能類別了。

音樂旋律、競速類的游戲跟其他類別交叉幾乎沒有,這兩種型別游戲形式比較單一,大部分都是拼手速操作,各種游戲同質性比較高。俠盜飛車是其中少有的融合了動作冒險的經典競速類游戲,但當年玩罪惡都市的快感似乎跟競速沒啥關係。

 

 

04 游戲平臺分析

 

游戲平臺型別非常多,整體分為手機、電腦、游戲機三類,游戲機大部分是任天堂(Wii,GB)、索尼(PS)、微軟(Xbox)的產品。

之前提到,一款游戲可能同時在多個平臺上發佈,這給分析過程帶來了一定難度,觀察後發現,豆瓣的平臺分佈是越靠前的平臺越大眾化,所以對於有多個平臺的游戲,取第一個平臺,視為他的主要發佈平臺進行分析。

1. 各平臺游戲數

鑒於平臺數太多,我們把所有游戲數目小於100的平臺彙總,記為“其他”,各個平臺游戲數分佈如下:

PC游戲數超過總數的50%,除此外,大部分游戲在iphone,PS2,PS3上,沒有Android的原因在於豆瓣上對於游戲平臺把iphone放在Android前面,大部分手游是在這兩個操作系統上同時發佈的,之前的處理方法導致Android數目非常少歸到了“其他”中去。

2. 各平臺均分

刪除無評分資料游戲後,各平臺均分基本一致。其中均分最高的GB是任天堂1989年推出的Game Boy 游戲機,GBA是任天堂2001年推出的Game Boy Advanced游戲機。你可能沒有用過這兩款設備,但當中的經典游戲你一定玩過。

3. 各平臺平均評分人數

各平臺評分總人數來說,PC占據絕對優勢,但平均人均數來看,人數最多的是PS4和Nintendo Switch。

 

 

05 游戲名稱分析

 

一個有意思的問題是,游戲都是怎樣命名的呢?有沒有什麼規律?

爬取下來的游戲名稱中大部分同時包含中文、英文,這裡我們只分析中文,將所有游戲名稱拼到一起用正則提取其中的中文,去掉長度為1的詞,和詞頻小於10的詞,對剩下的高頻詞按詞頻做詞雲如下:

詞語能反映出游戲的世界觀,大部分的游戲會用到戰爭、戰士、傳奇、聯盟、幻想這樣一些虛構的有奇幻色彩的詞語,同時也不乏三國、火影等等一些源於歷史、動漫、小說、電影作品的詞。還有一些開門見山直接說明游戲形式的詞語,比如迷宮、格鬥、大戰、足球等等。

 

 

06 高分游戲彙總

 

對游戲的整體分析只是統計分析的需要,但對一個游戲迷來說,只需要告訴他哪些游戲好就ok了,不好的游戲並不關註,我們提取所有游戲中評分超過9.5的部分,游戲型別分佈如下:

考慮到評分人數太少時,評分結果不一定具有代表性,所以我們只選擇其中評分人數超過100的部分,共84款游戲彙總如下,看看有沒有你玩過or你想玩的呢?

 

 

07 代碼彙總

 

1. 爬蟲代碼

# -*- coding: utf-8 -*-
import urllib
import requests
from fake_useragent import UserAgent
import json
import pandas as pd
import time
import datetime
import os

# 發送get請求
"""
genres : 游戲類別
n_ratings: 評分人數
platforms: 平臺
rating : 評分
content : 最熱評論
star : 星數
title : 游戲名稱
"""

def getDoubanGame(genres):
id_all1 = {1:"動作",5:"角色扮演",41:"橫版過關",4:"冒險",48:"射擊",32:"第一人稱射擊",
2:"策略",18:"益智",7:"模擬",3:"體育",6:"競速",9:"格鬥",37:"亂鬥/清版",12:"即時戰略",
19:"音樂/旋律"}

comment_api = 'https://www.douban.com/j/ilmen/game/search?genres={}&platforms;=&q;=&sort;=rating&more;={}'

essay-headers = { "User-Agent": UserAgent(verify_ssl=False).random}

response_comment = requests.get(comment_api.format(genres,1),essay-headers = essay-headers)
json_comment = response_comment.text
json_comment = json.loads(json_comment)
col = ['name','star','rating','platforms','n_ratings','genres','content']

dataall = pd.DataFrame()


num = json_comment['total']
print('{}類別共{}個游戲,開始爬取!'.format(id_all1[genres],num))

i = 0
while i < num:

if i == 0:
s = 1
else:
s = json_comment['more']

response_comment = requests.get(comment_api.format(genres,s),essay-headers = essay-headers)
json_comment = response_comment.text
json_comment = json.loads(json_comment) 

n = len(json_comment['games'])
datas = pd.DataFrame(index = range(n),columns = col)
for j in range(n): 
datas.loc[j,'name'] = json_comment['games'][j]['title']
datas.loc[j,'star'] = json_comment['games'][j]['star']
datas.loc[j,'rating'] = json_comment['games'][j]['rating']
datas.loc[j,'platforms'] = json_comment['games'][j]['platforms']
datas.loc[j,'n_ratings'] = json_comment['games'][j]['n_ratings']
datas.loc[j,'genres'] = json_comment['games'][j]['genres']
datas.loc[j,'content'] = json_comment['games'][j]['review']['content']

i += 1
dataall = pd.concat([dataall,datas],axis = 0)
print('已完成 {}% !'.format(round(i/num*100,2)))
time.sleep(0.5)
dataall = dataall.reset_index(drop = True)
dataall['type'] = id_all1[genres]
return dataall

id_all = {"動作":1,"角色扮演" :5,"橫版過關" :41,"冒險" :4,"射擊": 48,"第一人稱射擊":32,
"策略":2,"益智":18,"模擬":7,"體育":3,"競速":6,"格鬥":9,"亂鬥/清版":37,"即時戰略":12,"音樂/旋律":19}


id_all1 = {1:"動作",5:"角色扮演",41:"橫版過關",4:"冒險",48:"射擊",32:"第一人稱射擊",
2:"策略",18:"益智",7:"模擬",3:"體育",6:"競速",9:"格鬥",37:"亂鬥/清版",12:"即時戰略",
19:"音樂/旋律"}

for i in list(id_all.values()):
dataall = getDoubanGame(i)
filename = '游戲類別_' + id_all1[i] +'.xlsx'
filename = filename.replace('/','')
dataall.to_excel(filename)

 

2. 資料總覽

dataall['n_platforms'] = dataall.platforms.apply(lambda x:len(str(x).split('/')))
dataall['platform'] = dataall.platforms.apply(lambda x:str(x).split('/')[0].strip())

sns.pairplot(dataall,diag_kind = 'kde') 
plt.show()

data2 = dataall.dropna()
sns.pairplot(data2,diag_kind = 'kde') 
plt.show()

 

3. 游戲型別分析

"""
各型別游戲分析
"""

# 各型別游戲數

num = len(dataall.name.unique())
result = dataall.groupby('type').count()['name'].reset_index().sort_values('name',ascending = False)


attr = list(result.type)
v = list(np.round(result.name/num,3)) 

pie = Pie()
style = Style()
pie_style = style.add(
label_pos="center",
is_label_show=True,
label_text_color=None,
is_legend_show = False
)
pie.add("",[attr[0],"其他"],[v[0],1-v[0]],radius=[18, 24],center = [10,20],**pie_style)
pie.add("",[attr[1],"其他"],[v[1],1-v[1]],radius=[18, 24],center = [30,20],**pie_style)
pie.add("",[attr[2],"其他"],[v[2],1-v[2]],radius=[18, 24],center = [50,20],**pie_style)
pie.add("",[attr[3],"其他"],[v[3],1-v[3]],radius=[18, 24],center = [70,20],**pie_style)
pie.add("",[attr[4],"其他"],[v[4],1-v[4]],radius=[18, 24],center = [90,20],**pie_style)

pie.add("",[attr[5],"其他"],[v[5],1-v[5]],radius=[18, 24],center = [10,50],**pie_style)
pie.add("",[attr[6],"其他"],[v[6],1-v[6]],radius=[18, 24],center = [30,50],**pie_style)
pie.add("",[attr[7],"其他"],[v[7],1-v[7]],radius=[18, 24],center = [50,50],**pie_style)
pie.add("",[attr[8],"其他"],[v[8],1-v[8]],radius=[18, 24],center = [70,50],**pie_style)
pie.add("",[attr[9],"其他"],[v[9],1-v[9]],radius=[18, 24],center = [90,50],**pie_style)

pie.add("",[attr[10],"其他"],[v[10],1-v[10]],radius=[18, 24],center = [10,80],**pie_style)
pie.add("",[attr[11],"其他"],[v[11],1-v[11]],radius=[18, 24],center = [30,80],**pie_style)
pie.add("",[attr[12],"其他"],[v[12],1-v[12]],radius=[18, 24],center = [50,80],**pie_style)
pie.add("",[attr[13],"其他"],[v[13],1-v[13]],radius=[18, 24],center = [70,80],**pie_style)
pie.add("",[attr[14],"其他"],[v[14],1-v[4]],radius=[18, 24],center = [90,80],**pie_style)


pie.render('各型別游戲數.html')


# 各型別游戲均分
c_schema= [( "亂鬥/清版",10),
( "體育",10),
( "冒險",10),
( "動作",10),
( "即時戰略",10),
( "射擊",10),
( "格鬥",10),
( "模擬",10),
( "橫版過關",10),
( "益智",10),
( "競速",10),
( "第一人稱射擊",10),
( "策略",10),
( "角色扮演",10),
( "音樂/旋律",10)]
result1 = dataall.rating.fillna(0).groupby(dataall.type).mean().reset_index()
result2 = dataall.rating.dropna().groupby(dataall.dropna().type).mean().reset_index()

v1 = [result1.rating.apply(lambda x:round(x,1)).tolist()]
v2 = [result2.rating.apply(lambda x:round(x,1)).tolist()]

radar = Radar()
radar.config(c_schema)
radar.add("游戲均分(無評分視為0)", v1, is_splitline=True, is_axisline_show=True,is_label_show = True)
radar.add("游戲均分(刪除無評分)", v2, label_color=["#4e79a7"],item_color="#f9713c",is_label_show = True)
radar.render('各型別游戲評分.html')


# 各型別游戲評分人數

result1 = dataall.n_ratings.fillna(0).groupby(dataall.type).mean().reset_index()
result2 = dataall.n_ratings.dropna().groupby(dataall.dropna().type).mean().reset_index()

attr = result1.type.tolist()
v1 = np.round(result1.n_ratings.tolist(),1)
v2 = np.round(result2.n_ratings.tolist(),1)
line = Line()
#line.add(x_axis = attr,y_axis = xaxis_type = 'category')
line.add("包含無評分", attr, v1, mark_point=["max"],is_label_show = True)
line.add("不包含無評分", attr, v2, is_smooth=True, mark_point=["max"],is_label_show = True,xaxis_rotate = 30)
line.render('各型別游戲-評分人數.html')

4. 游戲平臺分析

"""
游戲平臺分析
"""
# 各平臺游戲數

num = len(dataall.name.unique())
result = dataall.groupby('platform').count()['name'].reset_index()


name = result.platform.tolist()
value = result.name.tolist()
wordcloud = WordCloud(width=1300, height=620)
wordcloud.add("", name, value, word_size_range=[20, 120])
wordcloud.render('游戲平臺.html')


platforms = result.loc[result.name >100,'platform'].tolist()


dataall.platform = dataall.platform.apply(lambda x:x if x in platforms else '其他')
result = dataall.groupby('platform').count()['name'].reset_index().sort_values('name',ascending = False).reset_index(drop = True)


attr = result.platform
v1 = result.name
pie = Pie('各平臺游戲數',title_pos = 'center',title_text_size = 20)
pie.add(
"",
attr,
v1,
radius=[40, 75],center = [50,60],
label_text_color=None,is_legend_show = False,
is_label_show=True
)
pie.render('各平臺游戲數.html')






# 各平臺游戲評分人數

result1 = dataall.n_ratings.fillna(0).groupby(dataall.platform).mean().reset_index()
result2 = dataall.n_ratings.dropna().groupby(dataall.dropna().platform).mean().reset_index()

attr = result1.platform.tolist()
v1 = np.round(result1.n_ratings.tolist(),1)
v2 = np.round(result2.n_ratings.tolist(),1)
line = Line()
#line.add(x_axis = attr,y_axis = xaxis_type = 'category')
line.add("包含無評分", attr, v1, mark_point=["max"],is_label_show = True)
line.add("不包含無評分", attr, v2, is_smooth=True, mark_point=["max"],is_label_show = True,xaxis_rotate = 70)
line.render('各平臺游戲-評分人數.html')


# 各平臺游戲均分

result1 = dataall.rating.fillna(0).groupby(dataall.platform).mean().reset_index()
result2 = dataall.rating.dropna().groupby(dataall.dropna().platform).mean().reset_index()

attr = result1.platform.tolist()
v1 = np.round(result1.rating.tolist(),1)
v2 = np.round(result2.rating.tolist(),1)
line = Line()
#line.add(x_axis = attr,y_axis = xaxis_type = 'category')
line.add("包含無評分", attr, v1, mark_point=["max"],is_label_show = True)
line.add("不包含無評分", attr, v2, is_smooth=True, mark_point=["max"],is_label_show = True,xaxis_rotate = 70)
line.render('各平臺游戲-均分.html')

 

5. 游戲名稱分析

"""
標題分析

"""



# 分詞
import re
stopwords = open('中文停用詞表(比較全面,有1208個停用詞).txt','r').read()
stopwords = stopwords.split('\n')


texts = ''.join(dataall.name.tolist())
texts =''.join(re.findall(r'[\u4e00-\u9fa5]',texts))
result = jieba.cut(texts,cut_all=False)


allwords = [word for word in result if len(word)>1 and word not in stopwords]



result = pd.DataFrame(allwords)
result.columns =['word']
res = result.word.groupby(result.word).count()
res.index.name = 'text'
res = res.reset_index()
res = res.loc[res.word >= 10].reset_index(drop = True)

# 標題詞雲
name = res.text.tolist()
value = res.word.tolist()
wordcloud = WordCloud(width=1300, height=620)
wordcloud.add("", name, value, word_size_range=[10, 80])
wordcloud.render('游戲名稱高頻詞.html')

 

6. 高分游戲彙總

"""
高rating游戲分析
"""
 
data1 = dataall.loc[dataall.rating>=9.5]

result = data1.groupby('type').count()['name'].reset_index().sort_values('name',ascending = False).reset_index(drop = True)

attr = result.type
v1 = result.name
pie = Pie('9.5分以上游戲',title_pos = 'center',title_text_size = 20)
pie.add(
"",
attr,
v1,
radius=[40, 75],center = [50,60],
label_text_color=None,is_legend_show = False,
is_label_show=True
)
pie.render('高評分游戲-分型別.html')

data1['title'] = data1.name.apply(lambda x:str(x).split(':')[0].split(' ')[0])

# 9.5以上評分,評分人數超過1000
result = data1.loc[data1.n_ratings >= 100,['name','genres','content','platforms','rating','n_ratings']].drop_duplicates().reset_index(drop = True) 

result = result.sort_values(by = ['n_ratings','genres'],ascending = False).reset_index(drop = True)
result.to_excel('評分9.5以上游戲.xlsx')

    赞(0)

    分享創造快樂