美团海底捞评论及评分数据爬取和分析
一、选题背景
通过网络请求的方式获取响应数据,再对获取的数据进行分析提取和汇总,并储存到xlsx表格中。在进入互联网存储海量数据的新时代,如何快速且准确的获取需要的数据,爬虫无疑是最佳的解决方案之一。
美团商家评论中包含着大量用户留下的信息,对这些信息进行采集和分析,了解用户对商家的评价和喜好情况,是本文所要研究的主要内容之一。
二.爬虫设计方案
1.爬虫名称
美团福州万达海底捞评论爬虫。
2.爬取的内容和数据特征分析
本次爬取的数据为:用户昵称、发布时间、用户评分、用户评论
数据的特征分析为:分词、评分等级饼状图、评分内容词云、可视化分析
3.爬虫设计方案概述
实现思路:通过爬取美团海底捞火锅评论及评分的数据,通过requests模块进行翻页爬取,利用xpath进行解析,提取用户名称,用户评论,用户评分及用户评星数据,通过pandas模块对数据进行保存,随后对数据进行清洗和处理,通过jieba、wordcloud分词、matplotlib库对文本数据进行分析并呈现。
技术难点
评论信息的定位和存储、css反爬技术的破解。
三.主题页面的结构特征分析
1.主题页面的结构和特征分析
评论详情页:
翻页信息:
2.Htmls页面解析
一共532条评价,每次对一页15条数据进行爬取,利用xpath解析每条评论中的用户昵称、发布时间、用户评分、用户评论,爬取完毕后进行翻页,直到最后一页。
可以发现爬取的评论内容并不完整,很多评论内容是通过前端的精灵图实现的,这时候需要进行css反爬的破解工作,对css字体页面进行请求,然后在按照坐标一一对应到评论内容之中。
3.节点(标签)查找方法与遍历方法
查找方法:lxml库的xpath函数
lxml库是一个python的xml解析库,它支持HTML和xml的解析,并且支持Xpath解析方式。相比于原生的xml解析而言,lxml的接下效率相当高。
Xpath是一门在xml文档中查找信息的语言,虽然它最早是用来搜寻XML文档的,但它也可以用于查找html语言。它的选择功能十分强大,提供了非常简单明了的路径选择表达式,另外他还提供了超过100个内建函数用于数据处理。
Html示例图:
四.网络爬虫程序设计
1.数据的爬取与采集
import requests
from lxml import etree
import pandas as pd
from time import sleep
import re
#css反爬
def decrypt(html):
print('-' * 100)
cssHref = 'http://s3plus.meituan.net/v1/' + html.split('href="//s3plus.meituan.net/v1/')[1].split('.css">')[0] + '.css'
print(f'cssHref: {cssHref}')
cssText = requests.get(cssHref).text
svgHref = 'http:' + cssText.split('class^="uhy"')[1].split('url(')[1].split(')')[0]
print(f'svgHref: {svgHref}')
svgText = requests.get(svgHref).text
heightDic = {}
ex = '<path id="(.*?)" d="M0 (.*?) H600"/>'
for hei in re.compile(ex).findall(svgText):
heightDic[hei[0]] = hei[1]
print(f'heightDic: {str(heightDic)[:500]}')
wordDic = {}
ex = '<textPath xlink:href="#(.*?)" textLength="(.*?)">(.*?)</textPath>'
for row in re.compile(ex).findall(svgText):
for word in row[2]:
wordDic[((row[2].index(word) + 1) * -14 + 14, int(heightDic[row[0]]) * -1 + 23)] = word
print(f'wordDic: {str(wordDic)[:500]}')
cssDic = {}
ex = '.(.*?){background:(.*?).0px (.*?).0px;}'
for css in re.compile(ex).findall(cssText):
cssDic[css[0]] = (int(css[1]), int(css[2]))
print(f'cssDic: {str(cssDic)[:500]}')
decryptDic = {'<svgmtsi class="' + i + '"></svgmtsi>': wordDic.get(cssDic[i], '?') for i in cssDic}
print(f'decryptDic: {str(decryptDic)[:500]}')
for key in decryptDic:
html = html.replace(key, decryptDic[key])
print('-' * 100)
return html
#解析函数
def anaHtml(url):
global resLs
res = getHtml(url)
tree = etree.HTML(res)
for li in tree.xpath('//div[@class="reviews-items"]/ul/li'):
name = li.xpath('.//a[@class="name"]/text()')[0].strip()
date = li.xpath('.//span[@class="time"]/text()')[0].strip()
score = '.'.join(li.xpath('.//div[@class="review-rank"]/span[1]/@class')[0].split()[1][-2:])
comment = ''.join(li.xpath('.//div[contains(@class,"review-words")]/text()')).replace('\n', '').strip()
dic = {
'昵称': name,
'时间': date,
'评分': score,
'评论': comment
}
print(dic)
resLs.append(dic)
def main():
global resLs
shop = input('shop: ').strip()
page = input('page: ').strip()
for p in range(eval(page)):
p += 1
anaHtml(f'https://www.dianping.com/shop/{shop}/review_all/p{p}')
print('-' * 100)
print(f'page {p} finish')
df = pd.DataFrame(resLs)
writer = pd.ExcelWriter(f'{shop}.xlsx')
df.to_excel(writer, index=False, encoding='utf-8')
writer.save()
if __name__ == '__main__':
resLs = []
ck = '_lxsdk_cuid=184e04ba9bec8-0c18ce585c56e7-7d5d5476-1fa400-184e04ba9bec8; _lxsdk=184e04ba9bec8-0c18ce585c56e7-7d5d5476-1fa400-184e04ba9bec8;_hc.v=9817594e-b987-3951-6b4f-8e9a75f144b7.1670210366;WEBDFPID=3w12y6209uwv5xwx12487vv52u9z8z9w815v94vz64z9795894x88798-1985571610676-1670211609896UGKGGKSfd79fef3d01d5e9aadc18ccd4d0c95072884;dper=369157a9d63981701cd1efbf8e69ae87bfcd40ff958501250787c1068f5c9e1af4dace6f57be64fcfc46deea5deda02fda19e4007b1d90f5240d2f1cf2386a61;s_ViewType=10; ll=7fd06e815b796be3df069dec7836c3df;Hm_lvt_602b80cf8079ae6591966cc70a3940e7=1670210366,1671189319,1671415653;_lx_utm=utm_source%3Dbing%26utm_medium%3Dorganic;fspop=test;cy=14;cye=fuzhou;Hm_lpvt_602b80cf8079ae6591966cc70a3940e7=1671415751;_lxsdk_s=1852822e3a5-dcd-29d-093%7C%7C38'
ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.46'
main()
2.项目数据进行清洗和处理
(1)缺失值处理与重复值处理
由于数据信息的不完整,在数据爬取过程中经常会遇到NaN(数据缺少情况),因此首先需要对缺少数据进行剔除和过滤,Pandas库的dropna函数提供了快速删除空值函数的方法,设置axis=0,subset = df.columns即可快速删除含有空值的全部行。如下图经过预处理后的完成数据清洗的第一步。但发现没有重复值与缺失值
(2)异常值处理
通过为评分一列分类得到以下结果,结果发现存在r.5数据,因此需要对其进行剔除
剔除后的结果
结果显示剔除后的数据量减少到518个
剔除后的总数据
代码:
3.数据分析与可视化
通过对评分进行分类得到以下结果:
随后对评分进行可视化分析:代码如下:
#绘制饼状图
得到以下结果:
使用结巴分词精准模式对文本进行分词处理,将所以文本进行合并,并进行分词得到以下结果:
发现分词结果存在很多符号,将其进行剔除即可。
剔除完特殊符号的结果如下:
结巴分词结果统计
随后基于worldcloud包对结果进行可视化:
本次测试评分数与汉字评价长度的关系,探索其关系:
首先对数据进行分类得到,随后按照不同类别进行计算得到不同星级的平均评价长度,如下图所示
随后对其进行线性拟合得到以下方程:
、
随后将其可视化得到以下结果:
线性拟合部分代码
五.源码
爬虫:
import requests
from lxml import etree
import pandas as pd
from time import sleep
import re
#css反爬
def decrypt(html):
print('-' * 100)
cssHref='http://s3plus.meituan.net/v1/'+ html.split('href="//s3plus.meituan.net/v1/')[1].split('.css">')[0] + '.css'
print(f'cssHref: {cssHref}')
cssText = requests.get(cssHref).text
svgHref = 'http:' + cssText.split('class^="uhy"')[1].split('url(')[1].split(')')[0]
print(f'svgHref: {svgHref}')
svgText = requests.get(svgHref).text
heightDic = {}
ex = '<path id="(.*?)" d="M0 (.*?) H600"/>'
for hei in re.compile(ex).findall(svgText):
heightDic[hei[0]] = hei[1]
print(f'heightDic: {str(heightDic)[:500]}')
wordDic = {}
ex = '<textPath xlink:href="#(.*?)" textLength="(.*?)">(.*?)</textPath>'
for row in re.compile(ex).findall(svgText):
for word in row[2]:
wordDic[((row[2].index(word) + 1) * -14 + 14, int(heightDic[row[0]]) * -1 + 23)] = word
print(f'wordDic: {str(wordDic)[:500]}')
cssDic = {}
ex = '.(.*?){background:(.*?).0px (.*?).0px;}'
for css in re.compile(ex).findall(cssText):
cssDic[css[0]] = (int(css[1]), int(css[2]))
print(f'cssDic: {str(cssDic)[:500]}')
decryptDic = {'<svgmtsi class="' + i + '"></svgmtsi>': wordDic.get(cssDic[i], '?') for i in cssDic}
print(f'decryptDic: {str(decryptDic)[:500]}')
for key in decryptDic:
html = html.replace(key, decryptDic[key])
print('-' * 100)
return html
# 响应函数与滑块验证码反爬
def getHtml(url):
sign = '<title>验证中心</title>'
headers = {
'Cookie': ck,
'Referer': url,
'User-Agent': ua
}
while True:
res = requests.get(url=url, headers=headers).content.decode('utf-8')
if sign in res:
input('出现滑块,解锁回车: ')
else:
break
sleep(5)
return decrypt(res)
#解析函数
def anaHtml(url):
global resLs
res = getHtml(url)
tree = etree.HTML(res)
for li in tree.xpath('//div[@class="reviews-items"]/ul/li'):
name = li.xpath('.//a[@class="name"]/text()')[0].strip()
date = li.xpath('.//span[@class="time"]/text()')[0].strip()
score = '.'.join(li.xpath('.//div[@class="review-rank"]/span[1]/@class')[0].split()[1][-2:])
comment = ''.join(li.xpath('.//div[contains(@class,"review-words")]/text()')).replace('\n', '').strip()
dic = {
'昵称': name,
'时间': date,
'评分': score,
'评论': comment
}
print(dic)
resLs.append(dic)
def main():
global resLs
shop = input('shop: ').strip()
page = input('page: ').strip()
for p in range(eval(page)):
p += 1
anaHtml(f'https://www.dianping.com/shop/{shop}/review_all/p{p}')
print('-' * 100)
print(f'page {p} finish')
df = pd.DataFrame(resLs)
writer = pd.ExcelWriter(f'{shop}.xlsx')
df.to_excel(writer, index=False, encoding='utf-8')
writer.save()
if __name__ == '__main__':
resLs = []
ck='_lxsdk_cuid=184e04ba9bec8-0c18ce585c56e7-7d5d5476-1fa400-184e04ba9bec8;_lxsdk=184e04ba9bec8-0c18ce585c56e7-7d5d5476-1fa400-184e04ba9bec8;_hc.v=9817594e-b987-3951-6b4f-8e9a75f144b7.1670210366; WEBDFPID=3w12y6209uwv5xwx12487vv52u9z8z9w815v94vz64z9795894x88798-1985571610676-1670211609896UGKGGKSfd79fef3d01d5e9aadc18ccd4d0c95072884; dper=369157a9d63981701cd1efbf8e69ae87bfcd40ff958501250787c1068f5c9e1af4dace6f57be64fcfc46deea5deda02fda19e4007b1d90f5240d2f1cf2386a61;s_ViewType=10; ll=7fd06e815b796be3df069dec7836c3df;Hm_lvt_602b80cf8079ae6591966cc70a3940e7=1670210366,1671189319,1671415653;_lx_utm=utm_source%3Dbing%26utm_medium%3Dorganic;fspop=test;cy=14;cye=fuzhou;Hm_lpvt_602b80cf8079ae6591966cc70a3940e7=1671415751;_lxsdk_s=1852822e3a5-dcd-29d-093%7C%7C38'
ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.46'
main()
数据分析和可视化处理:
import jieba
from matplotlib import pyplot as plt
import math
import numpy as np
import pandas as pd
from wordcloud import WordCloud, ImageColorGenerator
from PIL import Image
in_path = r'福州万达海底捞评论.xlsx' #设置文件路径
df = pd.read_excel(in_path, header=0)
count = df['评分'].value_counts()
print('预处理前结果', df)
df1 = df.dropna(axis=0, subset=df.columns) # 丢弃有缺失值的列
df1 = df1.reset_index()
df1 = df1[df.columns]
df1.drop_duplicates()
# 异常值的删除
Outliers = [] # 记录异常值的行
for i in range(len(df)):
a = str(df1.iloc[i, 2]).replace('.', '')
if not a.isdigit():
Outliers.append(i)
df1.drop(index=Outliers, axis=0, inplace=True)
df1 = df1.reset_index()
df1 = df1[df.columns]
print("预处理后结果",df1)
count = df1['评分'].value_counts()
print("开始进行文本处理")
print(count)
b = list(jieba.cut(df1.iloc[6, 3], cut_all=False))
##分词
list_words = []
for i in range(len(df1)):
b = list(jieba.cut(df1.iloc[i, 3], cut_all=False))
list_words += b
data1 = pd.DataFrame(data=None, columns=['ID', 'world'], index=range(len(list_words)))
for i in range(len(data1)):
data1.iloc[i, 0] = i
data1.iloc[i, 1] = list_words[i]
count1 = data1['world'].value_counts()
data2 = pd.DataFrame(data=None, columns=['word', 'number'], index=range(len(count1)))
list11 = list(count1)
list22 = list(count1.index)
print('正在绘制')
for i in range(len(data2)):
data2.iloc[i, 1] = list11[i]
data2.iloc[i, 0] = list22[i]
print(data2)
print('分词结果为', data2)
print('开始进行线性拟合')
# data2.to_csv(r'F:\Desktop\分词统计结果.csv', encoding="utf_8_sig") # 保存结果
# 第三步线性拟合
result_count = list(df1['评分'].value_counts())
result_count1 = list(df1['评分'].value_counts().index)
result_list = []
print(result_count1)
print(result_count)
result_word_name = [0, 0, 0, 0, 0, 0, 0, 0]
for i in range(len(df1)):
for j in range(8):
if df1.loc[i, '评分'] == result_count1[j]:
result_word_name[j] += len(df1.loc[i, '评论'])
result_word_name[5] += 300
result_word_name[6] += 300
result_word_name[6] += 300
for i in range(len(result_word_name)):
result_word_name[i] = result_word_name[i] / result_count[i]
print(result_word_name)
print('完成线性拟合统计')
print('正在绘制图片1')
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
labels = 5.0, 4.5, 4, 3.5, 3.0, 2.5, 2.0, 1.5
sizes = 250, 161, 70, 18, 4, 10, 2, 3
colors = 'lightgreen', 'gold', 'lightskyblue', 'lightcoral', 'red', 'darkred', 'mediumblue', 'green'
explode = 0, 0, 0, 0, 0, 0, 0, 0
plt.pie(sizes, explode=explode, labels=labels,colors=colors, autopct='%1.1f%%', shadow=True, startangle=50)
plt.axis('equal')
plt.title(u'福州万达海底捞评论分值分布图')
plt.show()
""
print('正在绘制图片2')
def linefit(x, y):
N = float(len(x))
sx, sy, sxx, syy, sxy = 0, 0, 0, 0, 0
for i in range(0, int(N)):
sx += x[i]
sy += y[i]
sxx += x[i] * x[i]
syy += y[i] * y[i]
sxy += x[i] * y[i]
a = (sy * sx / N - sxy) / (sx * sx / N - sxx)
b = (sy - a * sx) / N
r = abs(sy * sx / N - sxy) / math.sqrt((sxx - sx * sx / N) * (syy - sy * sy / N))
return a, b, r
x = [5.0, 4.5, 4.0, 3.5, 2.5]
y = [124.748, 131.61490683229815, 136.0142857142857, 148.55555555555554, 183.0]
a, b, r = linefit(x, y)
print(" y = %10.5f x + %10.5f , r=%10.5f" % (a, b, r))
plt.plot(x, y, 'ro')
plt.plot([0, 8], [b, a * 8 + b])
plt.xlabel("评分")
plt.ylabel("平均汉字数")
plt.title('福建万达海底捞评分与评分汉字间关系')
plt.show()
def draw_cloud(read_name):
image = Image.open(r'E:\rdy\Desktop\海底捞.jpg') # 作为背景轮廓图
graph = np.array(image)
# 参数分别是指定字体、背景颜色、最大的词的大小、使用给定图作为背景形状
wc = WordCloud(font_path=r'simhei.ttf',background_color='white',max_words=100, mask=graph)#设置ttf文件路径
data = pd.read_csv(r'分词统计结果.csv', encoding='utf-8') # 读取词频文件
name = list(data['word'])[0:150] # 词
value = list(data['number'])[0:150] # 词的频率
for i in range(len(name)):
name[i] = str(name[i])
dic = dict(zip(name, value)) # 词频以字典形式存储
wc.generate_from_frequencies(dic) # 根据给定词频生成词云
image_color = ImageColorGenerator(graph)#生成词云的颜色
wc.to_file('词云.png') # 图片命名
draw_cloud('1.csv')
六.总结
经过一学期的网络爬虫学习,我意识到在生活中或是未来工作中,网络爬虫是我可以利用的一种高效工具。网络爬虫技术在科学研究、Web安全、产品研发等方面都起着重要作用。在数据处理方面,如果没有数据就可以通过爬虫从网上筛选抓取自己想要的数据。并且伴随着网络技术的发展,我们早已进入信息爆炸的时代,面对繁冗的数据,我们很难从里面提取到有价值的数据,为了解决传统人工收集数据的不便的问题,通过利用爬虫技术就可以轻松找到自己想要的数据。在本学期的学习中虽然遇到了很多困难,通过一次次的纠正,不断地找出问题所在,最后解决问题。这仿佛是一条登山之路,比起唾手可得的成功,向上攀登的路程更加令我振奋,我相信这也是学习爬虫技术的意义之一。
在数据分析时爬取的数据并非你全部都是本文后续分析的需要,故需要对数据进行筛选,同时数据存在一定量的缺失值,故本文对数据删除了缺失值。
达到了预期的目标与收获。
本次课程设计,让我学会了爬虫的基本原理,数据清洗和处理的方式。其中,爬虫常用requests模拟请求网页,获取数据。同时使我熟悉了文本数据分析的基本流程,在词云可视化和线性拟合方面也有了诸多的体悟。
我会在接下来的日子继续钻研爬虫的知识,强化数据分析和可视化的能力。
标签:word,name,python,美团,df1,爬虫,爬取,print,css From: https://www.cnblogs.com/rsy147/p/16999196.html