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

爬了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)

    分享創造快樂