先说说背景吧: 之前玩了一段时间的《跨越星弧》,后来太忙了就没玩了,最近突然想起来想看看,发现TapTap评分居然掉到7.7分了 其实我觉得这个产品挺好的,玩法、剧情、美术都有可圈可点之处。但是为什么突然就从8.5分+掉到7.7了呢
于是我就去翻了翻评论,翻了10+页,好像也没看出什么问题。也没兴致往下看了,因为评论真的太多了,这样人工一条条的看,根本看不出个所以然来
刚好最近在看游戏数据分析,于是就想到,要不自己做个爬虫扒一下评论数据吧
项目源码已上传至GitHub项目——Tap-Comment-Scrapy,欢迎查看和下载源码。(使用Jupyter Notebook环境开发)
///以下是正文///
为了分析需要,我们要爬取的信息包括【评论文本】、【评论分数】、【评论时间】,在TapTap的页面中基本是按块呈现的
在页面中按F12可以查看页面的源码,这里很重要的是要【找到对应模块的类名】 这里用到的爬虫的基本原理就是:
step1 加载url源码 step2 从里面找到我们需要的信息所在的类 step3 通过正则匹配,获取我们需要的信息 step4 整理输出
这个过程的实现需要用到几个库,但核心的代码非常简单,只有几行。下面是代码
1、使用的库
import pandas as pd import numpy as np import requests import re from bs4 import BeautifulSoup import time as tmpandas和numpy是必备的,就不多说了;requests是加载url用的,用来模拟浏览器开网页的过程;re库是做正则匹配用的;BeautifulSoup是爬取用的,用来访问加载到的html数据;然后最后的time是为了统计计算时间,便于调试程序效率
2、准备要爬取的url和输出容器
url_1="https://www.taptap.com/app/85450/review?order=default&page=" #url开头 url_2="#review-list" #url结尾 page_total=5 #需要爬取的总页数 comment_out=[] #输出容器 score_out=[] date_out=[]这里是计划多页爬取,需要拼接出每页的url。为了方便调试只爬前5页,因此设置了总页数为5
3、单页爬取和循环
先定义3个爬取函数,用来获取【评论文本】、【评论分数】、【评论时间】3个关键信息
这几个函数也是爬取的关键代码,作简单说明
选中class:即前面截图提到的【模块的类名】,这里需要对源码进行仔细查看,通常选取目标模块所在的类,或者上一级的类名即可,例如".class1.class2"。注意是否有同名但不同层级的类,它们可能会导致结果出错
建立正则表达式:通常不需要很高阶的正则匹配,你只需要重复一部分内容,然后标注出哪部分是需要的就可以了。例如get_comment_text里的表达式指的是:【<div class(任意内容)data-review(任意内容)“contents”>(目标内容)</ div>】 结果出错时,通常时因为正则表达式写错了或者不能正确指代特定区域
进行正则匹配:这个很简单,按照格式把上面两个东西填进去就可以了
def get_comment_text(star_bs): comment = star_bs.select(".review-item-text ") #选中目标class,将从这个class中匹配内容 pattern_pinglun = '<div class.*?data-review.*?"contents">(.*?)</div>' #建立正则表达式,(.*?)是最后会截取出来的内容 pinglun = re.findall(pattern_pinglun, str(comment), re.S) #调用re库进行正则匹配,保存结果 return pinglun def get_comment_score(star_bs): comment = star_bs.select(".review-item-text ") pattern_score='<i class.*?"width: (.*?)px"></i>' score = re.findall(pattern_score, str(comment), re.S) score_num = [ int(x)/14 for x in score ] #抓出来的是字符串,转化为整型,并计算评分 return score_num def get_comment_date(star_bs): comment_header = star_bs.select(".review-item-text .item-text-header") pattern_date='data-dynamic-time=".*?">(.*?)</span>' date=re.findall(pattern_date, str(comment_header), re.S) return date接下来就是运行的主体代码,通过对目标数据进行循环爬取 每次循环的步骤为:【拼接url】—【加载url】—【爬取数据】—【保存数据】—【打印进度】
for j in range(page_total): #总共爬5页数据 t1=tm.time() pagenum=str(j) link=url_1+pagenum+url_2 #拼接每一页的url star = requests.get(link) #加载url star_bs = BeautifulSoup(star.text, "html.parser") #把url对象转化为美味汤 comment_tmp=get_comment_text(star_bs) #获取评论 score_tmp=get_comment_score(star_bs) #获取分数 date_tmp=get_comment_date(star_bs) #获取评论时间 comment_out.extend(comment_tmp) #装入输出容器 score_out.extend(score_tmp) date_out.extend(date_tmp) t2=tm.time() timing=t2-t1 #计时,用于调试 print('page %d grapped, %5.2f seconds used' % (j,timing)) #输出爬取进度输出结果为:
page 0 grapped, 0.87 seconds used page 1 grapped, 0.98 seconds used page 2 grapped, 1.16 seconds used page 3 grapped, 1.32 seconds used page 4 grapped, 1.09 seconds used这时候数据已经爬取完成了,但是他们存储在3个列表对象中,我们还需要处理一下结果
4、导出结果到excel 这里的思路是将列表对象转为数据框,然后通过pandas输出为excel文件 需要注意的一点是,需要【先将列表转为字典,再转为数据框】 代码如下:
result={"comment" : comment_out, "score" : score_out, "comment_date" : date_out} #先把列表转为字典 resultpd=pd.DataFrame(result) #再把字典转为pandas数据框 resultpd.to_excel('comment_star.xlsx') #输出excel文件可以观察一下爬取到的数据,是符合我们的预期的
输入:
resultpd.head()输出:
comment score comment_date 0 \n<p>抱着能玩到不思议迷宫和或者长生劫的科技版本来的。先上结论,非常失望。地图解谜和战斗... 3.0 2019-05-23 11:39:07 1 \n<p>讲道理 本身游戏做的还马马虎虎 个人喜欢的是装备造型(国殇)还有词条(虽然说概率真... 3.0 2019-05-22 23:14:05 2 \n<p>开服第一天至今,玩来玩去,也花了差不多一千左右了吧,刚刚第一个星期还不错各种主线,... 3.0 2019-05-23 17:05:11 3 \n<p>就我一个人卡的飞起么?,飞船战什么的,突然卡的不能操作,等好久后重新开始战斗,打完... 3.0 2019-05-23 10:59:18 4 \n<p>游戏刚出的时候就开始玩,当时和朋友们一起玩的不亦乐乎,最近几个版本以来,bug一个... 1.0 2019-05-19 20:45:145、数据分析 出于个人习惯,分析的这部分我就直接用excel的数据透视表来跑了
主要是看了一下随着日期,玩家每天的评论变化,其中红色柱状是总的评论数
这里我们发现几个非常有趣的点, (1)4.13前后出现了评分陡降的情况 (2)4月底评分持续低迷 这段时间肯定是发生了什么事情,所以才出现掉分的情况。具体的原因,我们可以通过对评论的内容进行挖掘探究,初步的想法是通过文本挖掘,分析这段时期评论的【文本关键词】,以及评论的【文本情感变化】
具体的分析,等之后有时间了再更新吧