超细致通用,爬取豆瓣游戏短评——以王者荣耀评论为例
写在前面
1.文章是用来给自己练手的,纯属自娱自乐 2.案例选的《王者荣耀》,但理论上说,适用与全部的豆瓣游戏短评爬取,只要稍微改动下爬其他的也行👌。 3.爬取的网页:https://www.douban.com/game/26655376/comments?start=0&sort=score
一、介绍
本项目利用的是简单的python爬虫技术,并没有多线程,换ip等骚操作。通过获取并解析豆瓣短评页面的html文件,拿到想要的数据,并且导出为excel文件,或者csv文件等,最后利用powerbi简单的可视化分析。
二、步骤
1.观察
1.1网页结构
打开网页,直接F12,鼠标滚动浏览,明确自己想要的数据。 只看第一条评论栏中,因为其他结构都是类似的。
令我感兴趣的就只有他的评论,评分,评价时间以及点赞人数。 在元素浏览框中,定位想要的数据,分析出大致的框架,对后面选取元素有帮助。 这里画了一个流程图帮助理解。
div 评论框
div 用户信息框
span 点赞
p 评论
昵称
评论日期
评分
手机型号
1.2网页层次逻辑
打开关于《王者荣耀》游戏短评的豆瓣评论 点开第一页评论地址为:https://www.douban.com/game/26655376/comments?start=0&sort=score 点开第二页评论地址为:https://www.douban.com/game/26655376/comments?start=20&sort=score 点开第三页评论地址为:https://www.douban.com/game/26655376/comments?start=40&sort=score
然后就是简单的评断,发现规律啦: 网页链接后面带有两个参数,第二个参数“sort”,工地英语翻译一下就知道是排序方式,一直不变,就不用管了。第一个参数“start”每增加一个页码,就多出20。 代入验证下发现,这个”start"就是页码数乘个20。只要换这个数就能得到不同页码的html文件了。
2.准备
2.1引入库
首先需要导入三个非常常用,更好用的三个库。 第一个是request,最基本获取网页的库。 第二个是解析网页的,相对于原生态,这个工具能大大简化爬取流程。 第三个就是pandas,数据预处理,导出xlst什么的非常方便。
import requests as rq
from bs4 import BeautifulSoup
import pandas as pd
2.2获取html文件并且解析
豆瓣短评这里用了一点反爬的手段,需要我们构造一个请求头。 经实验发现豆瓣现在不仅仅需要’User-Agent’了,还要其他参数。 这里为了省事就直接用了自己的cookie,而且存活的时间还挺长的,需要替换成你自己的。 然后get这个网页保存到“html”变量。
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36(KHTML,like Gecko) Chrome/94.0.4606.81 Safari/537.36 Edg/94.0.992.4','Cookie':'你自己的cookie'}
html=rq.get("https://www.douban.com/game/26655376/comments?start=0&sort=score",headers=headers)
使用BeautifulSoup解析这个html。
soup = BeautifulSoup(html.text,'lxml')
用soup.contents就可以看到网页的大致内容了。
3.选取
3.1具体分析
前面已经分析过网页的基本结构了,我们选取第一个评论框。 这里用的是
soup.find_all('div','info')[0]
第一个参数是标签名称,第二个参数根据属性筛选。 这行代码就是找到html所有带class=“info”的div标签,然后选取第一个。 也就是短评里的第一个评论框了。 至于为什么要找第一个评论框,因为我们要的数据类型都包含在里面了,后续再进一步选取的更方便点,因为整个逻辑就是获取不同页面,再获取页面的评论框,再获取框内的数据。 比对后发现:
- 这个2426就是点赞数了,在一个span里,包含在类别为digg的span中。
- 2017年7月2日,就是评论日期,在个类别为pubtime的span里。
- 很差,就是评分,比较特殊,是一个类别为allstar10的span的属性。
- p标签里的就是具体评论内容了。
按照这个逻辑,看看第二个和第三个评论框,发现其他都没有什么较大的变化,除了第三个。 可以看到,allstar根据评分高低而变化。 比如allstar10就是非常差,allstar20就是比较差。 根据评分标准推测,有五个档次的,后续都要考虑进去。
3.2 操作选取需要的单个元素
soup.find_all('div','info')[0].find('span','pubtime').get_text()
找到第一个评论框里的的第一个类别为pubtime的span并获取内容文字,得到’2017年7月2日’
soup.find_all('div','info')[0].find('span','digg').find('span').get_text()
找到第一个评论框里的的第一个类别为digg的span,再获取嵌套的span并获取内容文字,得到*‘2426’*
soup.find('span',{'allstar50','allstar40','allstar30','allstar20','allstar10'}).get('title')
第三个特殊处理 找到第一个评论框里的的含有类别’allstar50’,‘allstar40’,‘allstar30’,‘allstar20’,'allstar10’的span并获取元素title值,得到’很差’。 方法很多,比如这里位置比较特殊,一直是第三个,也可以按位置选。 考虑到类别少,我直接全列举了。
soup.find('span','short').get_text()
找到第一个评论框里的第一个类别为short的span并获取内容文字,得到 '周围几乎所有人都在玩,为了社交也没办法下了一个。抄袭现象太严重,不说LOL毕竟都自家的,dota2风暴守望都抄了个遍,还用历史人物命名英雄名字带歪历史人物风气。更可笑的是一个手机游戏居然还有职业联赛,看一群人低着头在那儿搓手机打比赛就觉得可笑。这种游戏火了真是中国游戏界的悲哀。’
4.页面
接下来就是页面操作了 具体逻辑:首先,一个从0到整个列表数量for循环,对每一个评论框都做一次取元素的过程,把四个元素整合成一个列表,最后把列表添加到data这个空列表里来。 后面发现,有些人没打分,或者没日期,导致报错就中断,但我想让他出错继续爬。 其中稍微进阶一点的try,except异常处理就很好的解决了这个问题。 try后面跟可能会出错的代码,except后面跟出错后怎么处理,然后继续执行。 比如第一个爬不到时间,就让time直接为‘null’,而不会中断了。
data=[]
for i in range(0,len(soup.find_all('div','info'))):
a=soup.find_all('div','info')[i]
try:
time=a.find('span','pubtime').get_text()
except:
time='null'
try:
like=a.find('span','digg').find('span').get_text()
except:
like='null'
try:
score=a.find('span',{'allstar50','allstar40','allstar30','allstar20','allstar10'}).get('title')
except:
score='null'
try:
preview=a.find('span','short').get_text()
except:
preview='null'
group=[time,like,score,preview]
data.append(group)
输出的data 这里方便看的话可以直接
pd.DataFrame(data)
5.整合
最后一步就是整合全部的页面。 写了个方法,具体见每一步注释。
def getData(m:int):
"""
m:需要爬取多少也页
返回
"""
index=0
data=[]
pddata=pd.DataFrame()
while index<m:
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36 Edg/94.0.992.4'
,'Cookie':'你的cookie'}
html=rq.get("https://www.douban.com/game/26655376/comments?start="+'start='+str(index*20)+"&sort=score",headers=headers)
soup = BeautifulSoup(html.text,'lxml')
for i in range(0,len(soup.find_all('div','info'))):
a=soup.find_all('div','info')[i]
try:
time=a.find('span','pubtime').get_text()
except:
time='null'
try:
like=a.find('span','digg').find('span').get_text()
except:
like='null'
try:
score=a.find('span',{'allstar50','allstar40','allstar30','allstar20','allstar10'}).get('title')
except:
score='null'
try:
preview=a.find('span','short').get_text()
except:
preview='null'
group=[time,like,score,preview]
data.append(group)
index+=1
print('爬取到'+str(index)+'页')
pddata=pd.DataFrame(data)
return pddata
6.修改升级
稍微修改了一下, m填你想爬的页数;site:https://www.douban.com/game/35236561/comments按照格式换成你想要的豆瓣游戏短评网站;path是想要导出的excel文件路径。 不出问题的话适用与全部的短评爬取。 完整的就直接放下面了,直接复制,改成你的cookie就能用了
def getDoubanShortPrewiew(m:int,site:str,path:str=''):
"""
m:需要爬取多少也页
site:短评网页地址
path:导出路径,如D:/data.xlsx
返回
"""
index=0
data=[]
pddata=pd.DataFrame()
while index<m:
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36 Edg/94.0.992.4'
,'Cookie':'你的cookie'}
html=rq.get(site+'?'+'start='+str(index*20)+"&sort=score",headers=headers)
soup = BeautifulSoup(html.text,'lxml')
for i in range(0,len(soup.find_all('div','info'))):
a=soup.find_all('div','info')[i]
try:
time=a.find('span','pubtime').get_text()
except:
time='null'
try:
like=a.find('span','digg').find('span').get_text()
except:
like='null'
try:
score=a.find('span',{'allstar50','allstar40','allstar30','allstar20','allstar10'}).get('title')
except:
score='null'
try:
preview=a.find('span','short').get_text()
except:
preview='null'
group=[time,like,score,preview]
data.append(group)
index+=1
print('爬取到'+str(index)+'页')
pddata=pd.DataFrame(data)
if path!='':
try:
pddata.to_excel(path)
except:
print('检查路径是否正确')
return pddata
如例子:英雄联盟手游
看到结尾了^-^
|