无极安卓网多重安全检测下载网站、值得信赖的软件下载站!

当前位置: 无极安卓网 >  攻略 >  爬取steam数据

爬取steam数据 Steam游戏数据分析教程python实现

时间:2023-05-15 09:15:31

小编:无极安卓网

爬取steam数据,近年来Steam游戏平台的用户数量不断增加,游戏种类也愈加丰富,这为研究Steam游戏市场提供了丰富的数据资源。而针对Steam游戏数据的分析,Python已经成为了一种非常流行的工具。在本篇文章中我们将学习如何使用Python爬取Steam游戏数据,并通过数据分析来了解Steam游戏市场的趋势和一些有价值的信息。无论是想要进行游戏开发还是投资游戏市场,这些数据和分析都将有着重要的意义。

Steam游戏数据分析教程python实现

分为两大部分

一.爬取steam游戏信息:需要有相关包‘requests’,‘bs4’,‘pandas’,‘re’

二.清洗数据及数据可视化:需要有相关包‘pandas’,‘re’,matplotlib’,‘pandas’,‘numpy’

一.爬取steam游戏信息

整体来说steam还是很好爬的,不加headers都可以成功get到页面,但是因为有的游戏页面进入前会验证年龄之类的,解决方法:重新抓重新定向的页面。我嫌麻烦,直接在账号设置里可以关闭提示,然后加headers就没有这个问题了。而且不需要代理池和限制频率,还是很友好的。

1.1 先爬热销游戏的列表

主要就提取游戏的链接和ID,转成dataframe然后保存。steam每个游戏都有自己的ID,比如csgo的链接是store.steampowered.com/app/730/CounterStrike_Global_Offensive/

730就是游戏的ID,后面就是游戏的名字,即使不加名字也是可以重定向到这个页面。本来是想通过链接提取游戏名字的,结果发现汉字名字在链接中不会显示,比如三国志14:

https://store.steampowered.com/app/872410/14/

的只会显示14,索性放弃,在后面1.2中再提取游戏名字。另外get的网址后面已经加上过滤DLC的参数了。

如果你是Python初学者:一共需要修改3个参数 一个是headers,一个是爬取页数n的值,以及保存路径path,然后run就可以了。

import requests
from bs4 import BeautifulSoup
import re
import pandas as pd

headers = {
    'Accept': '',
    'Accept-Encoding': '',
    'Accept-Language': '',
    'Cache-Control': '',
    'Connection': '',
    'Cookie':'',
    'Host': '',
    'Sec-Fetch-Mode': '',
    'Sec-Fetch-Site': '',
    'Sec-Fetch-User': '',
    'Upgrade-Insecure-Requests': '',
    'User-Agent': ''
}
#替换你自己的headers
n = 20
#n代表爬取到多少页
path = '1.xlsx'
#修改你的保存位置

def getgamelist(n):
    linklist=[]
    IDlist = []
    for pagenum in range(1,n):
        r = requests.get('https://store.steampowered.com/search/?ignore_preferences=1&category1=998&os=win&filter=globaltopsellers&page=%d'%pagenum,headers=headers)
        soup = BeautifulSoup(r.text, 'lxml')
        soups= soup.find_all(href=re.compile(r"https://store.steampowered.com/app/"),class_="search_result_row ds_collapse_flag")
        for i in soups:
            i = i.attrs
            i = i['href']
            link = re.search('https://store.steampowered.com/app/(\d*?)/',i).group()
            ID = re.search('https://store.steampowered.com/app/(\d*?)/(.*?)/', i).group(1)
            linklist.append(link)
            IDlist.append(ID)
        print('已完成'+str(pagenum)+'页,目前共'+str(len(linklist)))
    return linklist,IDlist

def getdf(n):#转df
    linklist,IDlist = getgamelist(n)
    df = pd.DataFrame(list(zip(linklist,IDlist)),
               columns =['Link', 'ID'])
    return df
if __name__ == "__main__":
    df = getdf(n)#n代表爬取到多少页
    df.to_excel(path)#储存

df是这个样子的:

爬取steam数据 Steam游戏数据分析教程python实现

1.2爬详细信息。基本思路是对df遍历每行数据。对于每行来说,用requests.get(Link),再用bs4解析,然后提取出关键字(名字,价格,好评率,评论等),然后写入df中。

def gamename(soup):   #游戏名字
    try:
        a = soup.find(class_="apphub_AppName")
        k = str(a.string)
    except:
        a = soup.find(class_="apphub_AppName")
        k = str(a.text)
    return k

def gameprice(soup):#价格
    try:
        a = soup.findAll(class_="discount_original_price")
        for i in a:
            if re.search('¥|free|免费', str(i),re.IGNORECASE):
                a = i
        k = str(a.string).replace('	', '').replace('\n', '').replace('\r', '').replace(' ', '')
    except:
        a = soup.findAll(class_="game_purchase_price price")
        for i in a:
            if re.search('¥|free|免费', str(i),re.IGNORECASE):
                a = i
        k = str(a.string).replace('	', '').replace('\n', '').replace('\r', '').replace(' ', '')
    return k

def taglist(soup):#标签列表
    list1=[]
    a = soup.find_all(class_="app_tag")
    for i in a:
        k = str(i.string).replace('	', '').replace('\n', '').replace('\r', '')
        if k == '+':
            pass
        else:
            list1.append(k)
    list1 = str('\n'.join(list1))
    return list1

def description(soup):  #游戏描述
    a = soup.find(class_="game_description_snippet")
    k = str(a.string).replace('	', '').replace('\n', '').replace('\r', '')
    return k

def reviewsummary(soup):   #总体评价
    a = soup.find(class_="summary column")
    try:
        k = str(a.span.string)
    except:
        k=str(a.text)
    return k

def getdate(soup):   #发行日期
    a = soup.find(class_="date")
    k = str(a.string)
    return k

def userreviewsrate(soup):#总体数量好评率
    a = soup.find(class_="user_reviews_summary_row")
    k = str((a.attrs)['data-tooltip-html'])
    return k

def developer(soup):   #开发商
    a = soup.find(id="developers_list")
    k = str(a.a.string)
    return k

def getreviews(ID):#获取评论
    r1 = requests.get(
        'https://store.steampowered.com/appreviews/%s?cursor=*&day_range=30&start_date=-1&end_date=-1&date_range_type=all&filter=summary&language=schinese&l=schinese&review_type=all&purchase_type=all&playtime_filter_min=0&playtime_filter_max=0&filter_offtopic_activity=1'%str(ID),headers=headers.timeout=10)
    soup = BeautifulSoup(r1.json()['html'], 'lxml')
    a = soup.findAll(class_="content")
    list1 = []
    for i in a:
        list1.append(i.text.replace('	', '').replace('\n', '').replace('\r', '').replace(' ', ','))
    k=str('\n'.join(list1))
    return k

def getdetail(x):
    tag, des, reviews, date, rate, dev, review,name,price = ' ', ' ', ' ', ' ', ' ', ' ', ' ',' ',' '
    global count
    try:
        r = requests.get(x['Link'], headers=headers.timeout=10)
    except:
        print('服务器无响应1')
        try:
            r = requests.get(x['Link'], headers=headers.timeout=10)
        except:
            print('服务器无响应2')
            try:
                r = requests.get(x['Link'], headers=headers.timeout=10)
            except:
                print('服务器无响应3')

    try:
        soup = BeautifulSoup(r.text, 'lxml')
        name = gamename(soup)
        tag = taglist(soup)
        des = description(soup)
        reviews = reviewsummary(soup)
        date = getdate(soup)
        rate = userreviewsrate(soup)
        dev = developer(soup)
        review = getreviews(str(x['ID']))
        price = gameprice(soup)
        print('已完成: '+name+str(x['ID'])+'第%d个'%count)
    except:
        print('未完成:  '+str(x['ID'])+'第%d个'%count)
        price = 'error'

    count += 1
    return name,price,tag,des,reviews,date,rate,dev,review


if __name__ == "__main__":
    df1 = pd.read_excel('1.xlsx')
    count = 1
    df1['详细'] = df1.apply(lambda x: getdetail(x), axis=1)
    df1['名字'] = df1.apply(lambda x: x['详细'][0], axis=1)
    df1['价格'] = df1.apply(lambda x: x['详细'][1], axis=1)
    df1['标签'] = df1.apply(lambda x: x['详细'][2], axis=1)
    df1['描述'] = df1.apply(lambda x: x['详细'][3], axis=1)
    df1['近期评价'] = df1.apply(lambda x: x['详细'][4], axis=1)
    df1['发行日期'] = df1.apply(lambda x: x['详细'][5], axis=1)
    df1['近期数量好评率'] = df1.apply(lambda x: x['详细'][6], axis=1)
    df1['开发商'] = df1.apply(lambda x: x['详细'][7], axis=1)
    df1['评论'] = df1.apply(lambda x: x['详细'][8], axis=1)
    
    df1.to_excel('2.xlsx')
    print('已完成全部')

大概说几句,各个函数中实在懒得给变量起名字了。评价指标选择的是近期,不是总体。因为我没想到能够用apply函数同时,在dataframe中写入多列的方法。所以发送一次get请求后把各类信息作为元组打包写入['详细'],最终还要把['详细']里的元组再依次提取出来写入各列。结果如下图,

爬取steam数据 Steam游戏数据分析教程python实现

我一共爬了一万个游戏,不同时期获取的结果也会有所不同,因为地区限制等原因某些游戏不在国区卖,所以是直接df中drop还是自备梯子就取决于你自己了。

1.3爬在线人数

steam自己有个数据统计的网页,直接while循环+time.sleep简单粗暴,每10分钟以当前时间为名把数据直接保存为xlsx格式。我直接丢vps上24小时跑了。

while True:
    try:
        r = requests.get('https://store.steampowered.com/stats/Steam-Game-and-Player-Statistics?l=schinese',headers = headers ,timeout=15)
        if r.status_code == 200:
            soup = BeautifulSoup(r.text, 'lxml')
            a = soup.findAll(class_="player_count_row")
            NOW =[]
            MAX=[]
            ID=[]
            for i in a :
                NOW.append(str(i.contents[1].span.string))
                MAX.append(str(i.contents[3].span.string))
                ID.append(re.search('\d+',str(i.contents[7].a.attrs['href'])).group())
            df = pd.DataFrame(list(zip(NOW, MAX,ID)),
                           columns =['now','max', 'ID'])
            df1 = df.set_index('ID')
            path_stats = str(time.strftime("%Y年%m月%d日%H时%M分%S秒",time.localtime()))+'.xlsx'

            df1.to_excel(path_stats)
            print(path_stats)
            time.sleep(600)
    except:
        pass
二.清洗数据及数据可视化:2.1数据清洗

这部分相对简单,因为爬取的数据已经相对来说比较规范了。由于我忘记备份最初始的原始文档了,所以以下范例只用200条游戏数据做的。

首先

import pandas as pd
import re

然后读取之前爬虫的结果

path = ''
df = pd.read_excel(path,index_col=0)
df.info()#看一下数据结构

结果如下

首先看一下价格这一列

爬取steam数据 Steam游戏数据分析教程python实现

实际在爬虫爬取的游戏中,大多数都是价格。少部分是‘免费游玩’‘Free’之类的,极少部分是游戏demo,游戏demo这个就很烦,还要根据实际情况判断游戏价格,由于数量极少,直接归为免费游戏。

def price(x):
    try:
        pricenum = int(x['价格'].replace('¥',''))
    except:
        pricenum = 0
    return pricenum
df['价格'] = df.apply(lambda x:price(x),axis=1)
df['价格'] = pd.to_numeric(df['价格'])#转为int64
df.info()

结果如下,可以看到价格一列已经变成int64

接下来提取评价人数和好评率

def getreviewsnum(x):
    x1 = x['总体数量好评率']
    x2 = x['总体评价']
    if re.search('过去 30 天内的 (.*?) 篇用户评测中有 (\d*%) 为好评。',x1):
        num = re.search('过去 30 天内的 (.*?) 篇用户评测中有 (\d*%) 为好评。',x1).group(1)
    elif re.search('(\d*) 篇用户的游戏评测中有 (\d*%) 为好评。',x1):
        num = re.search('(\d*) 篇用户的游戏评测中有 (\d*%) 为好评。',x1).group(1)
    elif re.search('\d* 篇用户评测',x2):
        num = re.search('(\d*) 篇用户评测',x2).group(1)
    else:
        num = '0'
    return num
def getreviewsrate(x):
    x = x['总体数量好评率']
    if re.search('过去 30 天内的 (.*?) 篇用户评测中有 (\d*%) 为好评。',x):
        rate = re.search('过去 30 天内的 (.*?) 篇用户评测中有 (\d*%) 为好评。',x).group(2)
    elif re.search('(\d*) 篇用户的游戏评测中有 (\d*%) 为好评。',x):
        rate = re.search('(\d*) 篇用户的游戏评测中有 (\d*%) 为好评。',x).group(2)
    else :
        rate=''
    return rate
df['评价数量']=df.apply(lambda x:getreviewsnum(x),axis=1)
df['好评率']=df.apply(lambda x:getreviewsrate(x),axis=1)
df

结果如下,可以看到后面多了2列

爬取steam数据 Steam游戏数据分析教程python实现

然后同价格一样,转一下格式

df['评价数量'] = df['评价数量'].apply(lambda x:x.replace(',',''))
df['好评率'] = df['好评率'].apply(lambda x:str(x).replace('%',''))
df['评价数量'] = pd.to_numeric(df['评价数量'])
df['好评率'] = pd.to_numeric(df['好评率'])
df['ID'] = df['ID'].astype('str')#这里顺路把ID转为str
df.to_excel(path)

最后说一下发行时间这里,这里又有一些坑,因为存在部分数据没有日或者月的情况,所以我并没找到简便的方式把日期变成datetime格式。这里如果有大佬可以教我一下,不胜感激。

但是方法总是有的嘛,打开excel,把这一列改为短日期格式,但是并没有变化。这里如果双击一下那个单元格,发现变成了我们想要的格式,

怎么批量改呢,我在网上搜到这样的方式

数据→分列→下一步→下一步(选择日期)→完成

然后就ok了,5秒搞定。

这时我们重新读一下df发现已经变成datetime64格式了。

爬取steam数据 Steam游戏数据分析教程python实现

把爬取过程中产生的没用的列删掉,最后保存

df=df.drop('Unnamed: 0.1', axis=1)
df.to_excel(path)

关于游戏玩家实时数量的数据我打算攒够一个月再做清洗分析,等数据量够了我再补这部分的内容。

2.2 数据可视化

首先

#coding:utf-8
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from  matplotlib import cm
import datetime
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号

读取文件,把发行日期的年份提取到额外的一列['year']后面会用到,然后选取前一千行制作一份copy,选取评价数量为0的也就是未发售的游戏也制作一份copy。

dfraw = pd.read_excel(path,index_col=0)
dfraw['year'] = dfraw.apply(lambda x:str(x['发行日期'])[0:4],axis = 1)
df = dfraw.copy()
df_top1k = dfraw[0:1000].copy()
df_now = dfraw[dfraw['评价数量'].values!=0].copy()

看一下整体情况,这里我爬了一万条,但是有些未在国区发售,去除掉之后还剩9887条。

df.info()
df.describe()
爬取steam数据 Steam游戏数据分析教程python实现

可以看到平均游戏价格41.91,平均好评率79.28,画出一个散点图看一下分布情况

Y = df_now['价格'] # 每一个点的Y值
X = df_now['发行日期']# 每一个点的X值
plt.style.use('seaborn')#画布风格
plt.rcParams['font.sans-serif']=['Microsoft YaHei']#字体
plt.figure(figsize=(20, 5))#大小
#这里散点大小是热销排行的倒数,也就是说越热销的游戏,圆点也就越大
#颜色取决于好评率高低,colorbar也就是cmap选择'RdYlBu'风格
plt.scatter(X,Y, s=15000/(df_now.index+200), c=df_now['好评率'], alpha=.9,cmap=plt.get_cmap('RdYlBu'))
plt.colorbar().set_label('好评率',fontsize=20)
plt.xlabel('年份',fontsize=20)
plt.ylabel('价格',fontsize=20)
plt.show()
爬取steam数据 Steam游戏数据分析教程python实现

可以看到已发布的游戏绝大多数分布在2010年到2020年,0到200元以内,少部分游戏突破了400元。如果局部放大一下

Y = df_now['价格'] # 每一个点的Y值
X = df_now['发行日期']# 每一个点的X值
plt.style.use('seaborn')#画布风格
plt.rcParams['font.sans-serif']=['Microsoft YaHei']#字体
plt.figure(figsize=(20, 5))#大小
#这里散点大小是热销排行的倒数,也就是说越热销的游戏,圆点也就越大
#颜色取决于好评率高低,colorbar也就是cmap选择'RdYlBu'风格
plt.scatter(X,Y, s=15000/(df_now.index+200), c=df_now['好评率'], alpha=.9,cmap=plt.get_cmap('RdYlBu'))
datenow = datetime.datetime(2021,1,1)
dstart = datetime.datetime(2010,1,1)
plt.xlim(dstart, datenow)
plt.ylim(0, 500)
plt.xlabel('年份',fontsize=20)
plt.ylabel('价格',fontsize=20)
plt.colorbar().set_label('好评率',fontsize=20)
plt.show()
爬取steam数据 Steam游戏数据分析教程python实现

接下来以年分组进行平均价格和平均好评率的计算绘入图中,

df_yearprice = df.groupby('year')['价格'].mean().to_frame().reset_index().sort_values(by='year')#按年分组,求平均价格
df_yearreview = df.groupby('year')['好评率'].mean().to_frame().reset_index().sort_values(by='year')#按年分组,求平均好评率

plt.figure(figsize=(20, 5))
plt.plot(df_yearreview['year'],df_yearreview['好评率'], c='g',label='平均好评率%')
plt.plot(df_yearprice['year'],df_yearprice['价格'], c='c',label='平均价格')
plt.xlabel('年份',fontsize=20)
plt.legend()
plt.title('年份与价格、好评率')
plt.xlim(4,35)
plt.ylim(0, 100)
plt.show()
爬取steam数据 Steam游戏数据分析教程python实现

从1990年到2020年游戏平均价格从20多增长到50多,相对比,2019年2个月猪肉就能翻一倍。但这样对比其实并不科学,因为可以看出来2019年以前在2013年平均价格达到了峰值,之后因为涌入了大量的免(ke)费(jin)游戏以及游戏模式的改变(比如开箱子GO、DLC DAY 2等)导致游戏的入门价格平均值在不断的被拉低。好评率在30年内还算相对稳定。

接下来根据游戏类型做一个整体统计,根据我们爬的游戏标签对游戏做分类,首先做标签的词频统计,这里的思路就是把所有的标签作为一个list,然后遍历list统计为dict,然后做降序处理。这里绘2次图选择不同的数据源,一次全部一万个游戏,一次为前1000个游戏。以下代码是全部一万个,前1000只需要把所有df改为df_top1k就可以。

list1 = []
list1 = df['标签'].to_list()#全部一万个

list1 = '\n'.join(list1)
list1 =list1.split('\n')#把所有标签加入list1
frequency = {}
frequency1 = {}
for word in list1:#词频统计
    if word not in frequency:
        frequency[word] = 1
    else:
        frequency[word] += 1
frequency = sorted(frequency.items(),key = lambda x :x[1], reverse=True)#根据词频降序做排列输出一个元组
for i in frequency:
    frequency1[str(i[0])[0:2]+'\n'+str(i[0])[2:4]+'\n'+str(i[0])[4:6]+'\n'+str(i[0])[6:8]]=i[1]#元组转为字典,再让标签每隔2个字加\n,后面柱状图会用到
dffre = df.copy()
for i in  list(frequency)[0:50]:#检验50个tag覆盖率
    dffre = dffre[dffre['标签'].str.contains(i[0])== False]

print(len(dffre))

这里输出了21,说明50个游戏标签覆盖了绝大多数游戏,只有21个游戏没在这个范围内。然后绘图不再详细说了,跟前面差不多。

Y = list(frequency1.keys())[0:50]#取前50个标签
X = list(frequency1.values())[0:50]
plt.figure( figsize=(20, 5),)

plt.bar(Y,X, facecolor='#ff9999', edgecolor='white')

plt.xlabel('游戏类型',fontsize=20)
plt.ylabel('游戏数量',fontsize=20)
plt.xlim(-.5, 49.5)
for a,b in zip(Y,X):
    plt.text(a, b,int(b), ha='center', va= 'bottom',fontsize=10)
plt.show()
爬取steam数据 Steam游戏数据分析教程python实现数据源为全部的结果爬取steam数据 Steam游戏数据分析教程python实现数据源为热销榜前一千的结果

从整体上讲,都符合长尾型分布,独立 单人 动作 冒险这四种无论是否为热销游戏,都是热门标签。令我最吃惊的居然是单机的标签数量占比居然这么高,热销榜11.7%的游戏居然有裸露标签。

今天先更到这,后面改天再更

通过爬取Steam游戏数据,我们可以更深入地探索不同游戏之间的差异,了解玩家游戏习惯和偏好,进而提高游戏研发和销售的效率。Python作为一种广泛使用的编程语言,可以很好地帮助我们实现这一目标。希望这篇教程能够帮助更多的数据分析爱好者和游戏从业人员,共同促进游戏行业的发展。

相关文章

猜你喜欢

推荐游戏

更多