首页 > 编程语言 >python爬虫-美团海底捞评论及评分数据爬取和分析

python爬虫-美团海底捞评论及评分数据爬取和分析

时间:2022-12-22 17:23:05浏览次数:38  
标签:word name python 美团 df1 爬虫 爬取 print css

美团海底捞评论及评分数据爬取和分析

一、选题背景

  通过网络请求的方式获取响应数据,再对获取的数据进行分析提取和汇总,并储存到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

相关文章

  • centos安装python3
    1:使用CentOS自带的包管理器yum安装新版本的Python。例如,你可以使用以下命令安装最新的Python3版本:sudoyumupdatesudoyuminstallpython32:使用SCL库安......
  • python 数组字典转换
    将提交的数组字段一个字典 [ { "name":"name1", "age":"1", }, { "name":"name2", "age":"2", } ]#变成{ "name":"name1,name2",......
  • python字典转为对象,用"."方式访问对象属性
    python字典转为对象,用"."方式访问对象属性 params={"name":"login","params":{"transaction_id":"cc258bdb3dd4d6bba2","platformTy......
  • 用python写一个获取git log也就是changeLog的小工具
    一、前提:每次发版后,都是人工去整理gitlog进行发版说明,结合项目需要,决定写个小工具获取gitlog,主要实现的功能点有以下几点:1、获取gitcommit时的记录。2、在commit中......
  • Python 列表(List)
    目录列表列表简介列表的创建基本语法[]创建list()创建range()创建整数列表推导式生成列表列表元素的增加和删除append()方法+运算符操作extend()方法append与extend的区别:i......
  • Zeppelin-0.9.0安装并集成Hive、Spark、MySQL、Python
    1、下载安装包http://archive.apache.org/dist/zeppelin/zeppelin-0.9.0/2、上传zeppelin-0.9.0-bin-all.tgz至/opt/soft3、解压tar-zxvf/opt/soft/zeppelin-0.......
  • Python通过Jwt创建Token
    importtimeimportjwt#pipinstallPyJWTimporthashlibimportjsonimportbase64frompyDesimportdes,PAD_PKCS5,CBCSECRET_KEY='django-insecure-fr&......
  • ArcGIS Python 修改面的左上角为第一个点
    面的节点夹角大于30并且小于150,距离外界矩形左上角最近的点为第一个点,对于多部件每一个都修改,修改面的开始点位置,运行界面如图14-10所示。  #coding=utf8impor......
  • 教你用Python实现BMI计算器
    案例介绍欢迎来到我的小院,我是霍大侠,恭喜你今天又要进步一点点了!<br/>我们来用Python相关知识,做一个BMI计算器的案例。你可以通过控制台的提示信息,输入身高和体重,注意单......
  • python 虚拟环境搭建全流程
    首先,建立python虚拟环境test_env:python3-mvenvtest_env#激活虚拟环境source./test_env/bin/activate#linuxvenv/Scripts/activate#windows#停止虚拟环......