文档章节

steam夏日促销悄然开始,用Python爬取排行榜上的游戏打折信息

o
 osc_ur9mmbck
发布于 07/05 08:53
字数 2586
阅读 36
收藏 0

「深度学习福利」大神带你进阶工程师,立即查看>>>

 

前言

很多人学习python,不知道从何学起。
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!
QQ群:1097524789



本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。

不知不觉,一年一度如火如荼的steam夏日促销悄然开始了。每年通过大大小小的促销,我的游戏库里已经堆积满还未下载过的游戏。但所谓“买到就是赚到,G胖一定大亏”的想法日渐流行,指不定以后就靠它们发达了呢。

有时候滚动steam的排行榜看自己喜欢的游戏的时候,未免会被右边的价格影响到。久而久之我发现我所不想买的游戏并不是因为它不好玩,而是它还没打折。又或者有些心水未被别人挖掘,在排行榜隐秘的角落里自怨自艾,等待“把玩”它的人出现~

于是我简单的用python爬取了steam排行榜前10000个游戏的信息,其中有游戏名,评价,价格,出版日期等,在更加简洁的列表界面选取自己感兴趣的游戏之时,也可以进行进一步的数据分析。

废话不多说,赶紧开始,不然被我拖更到促销结束了就蹭不到热度了。(本来也没有热度)

 

开始爬取

先说说这次爬虫选用数据的优缺点:

第一,我发现了steam在显示排行榜列表的时候后台会进行一个查询的申请,点开一看是一串json代码,而且在python进行request的时候不需要模拟浏览器进行填“headers”表的操作。通过访问而得到的json代码大大简化了循环复杂度,一次循环可以得到100个游戏信息。

第二,因为只需要遍历所有json代码,时间上可以比进入每一个游戏链接更加短。

第三,但就因为没有进入每个游戏的链接,所以像评论,简介,开发商等信息就没有爬取。但爬取游戏链接的爬虫攻略网上也有很多,这里就不弄斧了。

 

首先,进入官网的排行榜页面,为了避免游戏DLC、bundle等影响后期操作的类型出现,记得在右边的过滤器里只勾选游戏类目。

 

通过后台的XHR发现,页面每次刷新都只显示前50个游戏,当我们滚动页面往下看时,网站会发送一个神秘代码:

 

经过观察,我发现代码会自动申请返回从start参数的数字开始,一共count参数的数字的游戏信息。比如,下面的图显示它申请了从第51个到100个总共50个游戏的信息。

 

双击上上图的红框链接,返回的页面长这样:

 

所谓json格式,其实就是在字典里夹字典或者列表,目前许多大数据都是这样保存滴。所以在查询的时候其实很方便,但是我在抽取信息的时候还是会用到正则表达式,因为会方便很多。

 

知道这些之后,剩下的就可以用python一个个有用信息抽取出来,组成一个新的Dataframe列表,以便之后保存为csv格式。

# 导入需要用到的库
import requests
from bs4 import BeautifulSoup
import re import json import numpy as np import pandas as pd import matplotlib.pyplot as plt

我们尝试用requests打开上面json页面的链接,并用json load解析。

这里我更改了start和count 的参数,比较方便对照原网页来看信息是否一致。

url = 'https://store.steampowered.com/search/results/?query&start=0&count=100&dynamic_data=&sort_by=_ASC&category1=998&snr=1_7_7_globaltopsellers_7&filter=globaltopsellers&infinite=1'
content = requests.get(url).content
jsontext = json.loads(content)
soup = BeautifulSoup(jsontext['results_html'],'html.parser')

可以看看soup返回的结果,它显示了json里边'results_html'返回的东西,因为前边的内容我们已经不需要了,所有游戏信息都在这个键里边。

 

接着我们回到那个json页面看看我们想要的东西都藏在哪:

游戏名字藏在span的title class里:

 

name = soup.find_all('span',class_ ='title')

出版日期藏在div的另一个class里:

 

listdate = soup.find_all('div', class_ ='col search_released responsive_secondrow')

同样的,可以用上面的方法找到游戏的链接、ID,这里就不赘述了。

 

评分和打分人数藏在span标签里,如果用字典查找的话会比较麻烦,所以我们稍后使用正则表达式将它俩提取出来:

 

不幸运的是,有些游戏因为还没上架,所以没有人评论,我们用正则表达式得到的信息是乱码。所以我们用函数来防止出现乱码的可能性:

 

def get_reviewscore(review):
    gamereview=[]
    for i in range(len(review)): try: score = re.search('br>(\d\d)%',str(review[i]))[1] except: score = '' gamereview.append(score) return gamereview ########################################### def get_reviewers(review): reviewers=[] for i in range(len(review)): try: ppl = (re.search('the\s(.*?)(\s)user',str(review[i]))[1]) except: ppl = '' reviewers.append(ppl) return reviewers

如果看到这里的读者觉得很轻松,那我便可以继续往下述说,因为爬取价格比评论更加麻烦。但仅限于麻烦,并没有很高大上的操作;而我相信我并不是用聪明的方法爬取到想要的结果,因为对于这个体量的数据再优化的代码对于运行时间来说相差不大。反正结果都一样,管它呢。

 

其实要找物品的最终价格(即免费游戏,打折后或未打折的游戏价格)非常简单,因为他就藏在这里:

 

默认后边两位为小数点后两位,所以我们直接把这串数字揪出来并除以100:

 

def get_finalprice(price):
    finalprice=[]
    for i in range(len(price)):
        pricelist = int(re.search('final(\W+?)(\d+)(\W)',str(price[i]))[2])/100 finalprice.append(pricelist) return finalprice

但我们如果就想知道他的原价,以便之后做分析该怎么办呢?

先看一下steam排行榜上的价格有三种显示方法:

 

第一种,带有划线价格的打折商品,在源代码中长这样:

 

 

第二种,免费的:

 

头疼的是,免费的标识也有变体:

 

 

 

 

 

 

(连to的大小写也有不一样的……steam您用点心!)

 

不过Free还是老老实实在最前面,所以我们后边只要找到Free就好啦。

 

第三种,原价显示:

 

上面的图片都是我在抽查的时候发现的规律与变形,为了避免后续几千个游戏有“乌合之众”,我在代码里只查找这三种格式,如果有奇形怪状的数据出现,直接一棍子打成“空值”:

def get_price(price):
    oripricelist=[]
    for i in range(len(price)): try: oripricelist.append(price[i].find_all(class_="col search_price responsive_secondrow")[0].text) except: oripricelist.append(price[i].find_all(class_="col search_price discounted responsive_secondrow")[0].text) ori_price=[] for i in range(len(oripricelist)): try: search = re.search('Free',oripricelist[i])[0].replace('Free','0') except: if oripricelist[i]== '\n': search='' else: try: search = re.search('HK.*?(\d+\.\d+)\D',oripricelist[i])[1] except: search='' ori_price.append(search) return ori_price 

 

定义完这些想要的数据之后,我们就开始跑循环了。

先把我们要的数据列命好名字:

def get_data(games=1000): 
    num_games = games
    gamename=[]
    gamereview=[]
    gamereviewers=[]
    gamerelease=[] oriprice=[] final_price=[] appid=[] website=[]

 

接着我们以每个链接查询100个游戏的步伐开始跑循环并将里边的信息找出来,录入上面的列表里:

 

       page = np.arange(0,num_games,100)
    for num in page: url = 'https://store.steampowered.com/search/results/?query&start='+str(num)+'&count=100&dynamic_data=&sort_by=_ASC&category1=998&snr=1_7_7_globaltopsellers_7&filter=globaltopsellers&infinite=1' print('the {} iteration: Trying to connect...'.format((num/100)+1)) content = requests.get(url).content jsontext = json.loads(content) soup = BeautifulSoup(jsontext['results_html'],'html.parser') name = soup.find_all('span',class_ ='title') review = soup.find_all('div', class_ ='col search_reviewscore responsive_secondrow') listdate = soup.find_all('div', class_ ='col search_released responsive_secondrow') price = soup.find_all('div', class_ = 'col search_price_discount_combined responsive_secondrow') href = soup.find_all(class_='search_result_row ds_collapse_flag') for i in name: gamename.append(i.text) getreview = get_reviewscore(review) for i in getreview: gamereview.append(i) getreviewers = get_reviewers(review) for i in getreviewers: gamereviewers.append(i) for i in listdate: gamerelease.append(i.text) getprice = get_price(price) for i in getprice: oriprice.append(i) getfinalprice = get_finalprice(price) for i in getfinalprice: final_price.append(i) for i in range(len(href)): appid.append(eval(soup.find_all(class_='search_result_row ds_collapse_flag')[i].attrs['data-ds-appid'])) website.append(soup.find_all(class_='search_result_row ds_collapse_flag')[i].attrs['href']) print('done')

 

我们在遍历中每次访问页面、完成每次循环的时候都让电脑打印一段字,以便出错的时候能快速找出出错的页面。

 

接下来就将得到的数据塞进一个数据表里:

    df = pd.DataFrame(data=[gamename,gamereview,gamereviewers,gamerelease,oriprice,final_price,appid,website]).T
    df.columns = ['name','review_score','reviewers','release_date','ori_price','final_price','id','link'] return df #呼叫我们的函数: df = get_data(10000) #这里的数字代表爬取10000个游戏

等待漫长的过程与欣赏成功的过程:

 

最后的数据集长这样:

 

接下来只要保存为csv格式,就可以开始分析数据了。但这已经不是爬虫文章的内容,所以不会往下继续分析啦。

 

总结与反思

我发现final_price也就是一开始提取的最终价格中,会有高于原价的现象。

比如CS:GO的最终价格并不是0,是因为它有一个升级包:

 

前1000个游戏里总共有3个这样的错误:

 

实况足球2020 是demo版免费,而想体验完整游戏确实需要78港币;

奇异人生1 是第一篇章免费,后边的篇章需要23.8港币。

 

这些代码跑起来虽然快,但得到的信息依旧太少,如果要深入研究steam的数据还是需要有强大的耐心遍历所有游戏链接呐。

 

这次的爬虫经历其实也发现了steam一些录入大数据库的时候的小差错,比如前面所提到的免费标识竟然有3种变体,但他们可能觉得问题不大。

 

终于整理结束,赶紧结尾:

这次的文章就到这里,希望对大家有所帮助~!

o
粉丝 0
博文 72
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
Flappy Bird(安卓版)逆向分析(一)

更改每过一关的增长分数 反编译的步骤就不介绍了,我们直接来看反编译得到的文件夹 方法1:在smali目录下,我们看到org/andengine/,可以知晓游戏是由andengine引擎开发的。打开/res/raw/at...

enimey
2014/03/04
6.1K
18
beego API开发以及自动化文档

beego API开发以及自动化文档 beego1.3版本已经在上个星期发布了,但是还是有很多人不了解如何来进行开发,也是在一步一步的测试中开发,期间QQ群里面很多人都问我如何开发,我的业余时间实在...

astaxie
2014/06/25
2.7W
22
Nutch学习笔记4-Nutch 1.7 的 索引篇 ElasticSearch

上一篇讲解了爬取和分析的流程,很重要的收获就是: 解析过程中,会根据页面的ContentType获得一系列的注册解析器, 依次调用每个解析器,当其中一个解析成功后就返回,否则继续执行下一个解...

强子哥哥
2014/06/26
712
0
在多个浏览器上运行脚本--Queen

假设你想和朋友们玩这么个游戏:你写下某个数字,然后让朋友们猜你写的是什么数字。你的朋友们将不断的给你一些猜测的数字,直到猜中为止。 现在想象你的朋友都是使用的浏览器,这个游戏就相...

匿名
2013/01/24
4.6K
1
跨平台 3D 游戏引擎--Castle Game Engine

Castle Game Engine 是一个用 Object Pascal 开发的跨平台 3D 游戏引擎。包含一个灵活的 3D 对象系统与开箱即用的水平,项目,智能生物等等。使用 X3D、VRML、Collada 和其他格式实现渲染和处理...

匿名
2013/02/05
2K
0

没有更多内容

加载失败,请刷新页面

加载更多

抖音批量发布软件,批量运营200+自媒体账号

抖音批量发布软件,批量运营200+自媒体账号,现在短视频平台大家也可以看的到,流量很大,很多人开通了商品橱窗,在上面进行带货,赚钱也很简单,尤其是抖音这个平台,男女老少都在玩,里面也...

osc_emgrwx5d
19分钟前
9
0
基于MES系统的企业信息化系统的集成

引言 随着网络和科技的发展,互联网技术和电子信息技术已经逐渐融入了我们生活工作的各个环节。在企业中,企业信息化程度的高低也直接影响到企业的运营。企业要想得到快速长远的发展,就要利...

osc_7owgvpdx
21分钟前
17
0
dockerfile源码编译安装nginx、php和harbor

安装docker 环境:基于CentOS Linux release 7.6.1810 注意:以下所有操作基于root用户 #centos7# step 1: 安装必要的一些系统工具yum install -y yum-utils device-mapper-persistent-da...

osc_otuqqtuq
21分钟前
13
0
条形码识别器Dynamsoft Barcode Reader v7.5全新上线!

Dynamsoft Barcode Readerv7.5带来了一些新的参数配置,例如中心定位条形码、二值化模式等。让我们来看看新功能中的一些亮点。 更新 添加了对QR Code Model 1(QR Code规范的旧版本)的支持。...

roffey
22分钟前
6
0
TCP端口的十一种连接状态

TCP端口的十一种连接状态∶ CLOSED∶端口默认是关闭状态。 
LISTEN∶服务器程序开始监听一个端口,就是LISTEN状态。 
 

SYN_RCVD∶三次握手的第二次握手后的端口状态,是收到了客户端发...

程序员面试吧
22分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部