當前位置:
首頁 > 知識 > Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

作者 | 量化小白H

責編 | 胡巍巍

本文爬取了豆瓣遊戲網站上所有可見的遊戲評分數據進行分析,全文包括以下幾個部分:

  1. 數據獲取
  2. 數據總覽
  3. 遊戲類型分析
  4. 遊戲平台分析
  5. 遊戲名稱分析
  6. 高分遊戲匯總
  7. 代碼匯總

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

數據獲取過程

數據來自豆瓣遊戲,網址:

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

頁面內一條遊戲數據展示如下,顯示出來的一條評論是遊戲的點贊數最多的評論,我們分析需要的數據包括遊戲名稱、遊戲類型、遊戲平台、遊戲評分、遊戲評價人數及最熱評價。

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

以動作類遊戲為例,動作類遊戲第二頁數據對應的網址如下:

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

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

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

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

之後就可以用python中的request+json包循環獲取數據了,代碼附在最後。爬到的一條遊戲數據樣式如下:

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

需要的數據包括:

  • n_ratings:評分人數
  • platforms:平台
  • rating:評分
  • star:星級
  • title:遊戲名稱
  • content:最熱評論

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


數據總覽

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

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

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

視為評分為0時:

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

刪除沒有評分的數據:

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

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

  1. 星級(star)和評分(rating)是嚴格單調關係的,星級是分級靠檔後的評分,所以之後的分析中只考慮星級不考慮評分。
  2. 兩種評分處理方法下,各變數的分布基本不變,大部分遊戲評分集中在7.5左右,評分跟發行平台數、遊戲名稱長度關係不是非常明顯。

遊戲類型分析

各類型遊戲數:

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

豆瓣把第一人稱射擊、射擊分成兩類,亂斗跟冒險、動作的區別似乎不是很大?這裡不是很理解。

不過射擊類的遊戲模式太過單一,數量確實比較少,高質量的射擊遊戲不外乎這兩年火起來的吃雞遊戲,還有很早之前的CS系列。

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

各類型遊戲平均評分人數:

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

各類型遊戲均分:

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

遊戲類型關聯分析:

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是


遊戲平台分析

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

各平台遊戲數:

鑒於平台數太多,我們把所有遊戲數目小於100的平台匯總,記為「其他」,各個平台遊戲數分布如下:

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

各平台均分:

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

各平台平均評分人數:

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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


遊戲名稱分析

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

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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


高分遊戲匯總

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是

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

Python爬取三萬條遊戲評分數據,原來程序員最愛玩的遊戲是


代碼匯總

爬蟲代碼:

# -*- 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={}"
headers = { "User-Agent": UserAgent(verify_ssl=False).random}
response_comment = requests.get(comment_api.format(genres,1),headers = 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),headers = 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)
數據總覽:
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()
遊戲類型分析:
"""
各類型遊戲分析
"""
# 各類型遊戲數
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")
遊戲平台分析:
"""
遊戲平台分析
"""
# 各平台遊戲數
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")
遊戲名稱分析:
"""
標題分析
"""
# 分詞
import re
stopwords = open("中文停用詞表(比較全面,有1208個停用詞).txt","r").read()
stopwords = stopwords.split("
")
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")
高分遊戲匯總:
"""
高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")

作者:量化小白一枚,上財研究生在讀,偏向數據分析與量化投資,個人公眾號量化小白上分記。
聲明:本文為作者投稿,版權歸其個人所有。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 CSDN 的精彩文章:

我是如何從零開始 Web 前端自學之路的?
如何用深度學習 AI 美顏實現天天 P 圖瘋狂變臉演算法? | 技術頭條

TAG:CSDN |