用Python爬取美团烤肉店数据并分析

用Python爬取美团烤肉店数据并分析_第1张图片
美国作家杜鲁门·卡波特曾说:“梦是心灵的思想,是我们的秘密真情。”在J哥的内心深处,也曾有一个小小的梦想,那就是开一家烤肉店。几串烤肉、一杯美酒,即可享受深夜路边的自由得意与平凡热辣的市井人生。

用Python爬取美团烤肉店数据并分析_第2张图片

可是,想开烤肉店可没那么容易,首先你得了解市场。于是,J哥打开了美团,一顿操作爬取了深圳所有的烤肉店数据,然后清洗数据并做可视化分析,试图摸到一点开烤肉店的门道。
用Python爬取美团烤肉店数据并分析_第3张图片

数据获取

美团网很明显是动态网页,需要通过解析接口或用Selenium爬取,如果您对动态网页爬虫感兴趣,可查看J哥往期原创文章「实战|Python轻松实现动态网页爬虫(附详细源码)」,本文通过解析接口的方法爬取数据。

找到真实URL

美团网URL:https://sz.meituan.com/
用Python爬取美团烤肉店数据并分析_第4张图片

分析真实URL

https://apimobile.meituan.com/group/v4/poi/pcsearch/30?uuid=你的&userid=-1&limit=32&offset=32&cateId=-1&q=%E7%83%A4%E8%82%89
主要参数:

  1. 30:城市id(30代表深圳)
  2. limit:每页店铺数量
  3. offset:翻页参数(每增加32翻页一次)
  4. q:关键字(%E7%83%A4%E8%82%89即烤肉)

由于美团限制一个地区只能爬取32页,也就是1024个店铺数据。为了获得更全面数据,可以找到areaId参数(子地区),然后遍历子地区,即可获得完整数据。本次爬取的数据范围为深圳烤肉店,包含店铺名称、店铺地址、人均消费、店铺评分、评论人数、 所在商圈、图片链接等字段。限于篇幅,仅给出核心代码。

def get_meituan():
    try:
        for areaId in areaId_list:
           for x in range(0, 2000, 32):
                time.sleep(random.uniform(2,4)) #设置睡眠时间
                print('正在提取areadId为%d的'%areaId,'第%d页'%int((x+32)/32))  #打印爬取进度
                url = 'https://apimobile.meituan.com/group/v4/poi/pcsearch/30?uuid=你的&userid=-1&limit=32&offset={0}&cateId=-1&q=%E7%83%A4%E8%82%89&areaId={1}'.format(x,areaId)
                print(url)
                headers = {
                    'Accept': '*/*',
                    'Accept-Encoding': 'gzip, deflate, br',
                    'Accept-Language': 'zh-CN,zh;q=0.9',
                    'Connection': 'keep-alive',
                    'Cookie':'你的',
                    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36',
                    'Host': 'apimobile.meituan.com',
                    'Origin': 'https://sr.meituan.com',
                    'Referer': 'https://sr.meituan.com/s/%E7%83%A4%E8%82%89/'
                }
                response = requests.get(url, headers=headers)
                print(response.status_code)

数据处理

短短几分钟就爬下了2万多个烤肉店信息,为了方便可视化分析,还需要对爬取的数据进行简单清洗。

本文数据清洗主要用到Python的Pandas库,如果您对Pandas感兴趣,可查看J哥往期原创专辑「Pandas基础系列」,共五篇。

导入数据

导入数据并添加列名,用sample()方法随机抽取5个样本数据预览。

import pandas as pd
import numpy as np
df = pd.read_csv('/Users/wangjia/Documents/技术公号/公号项目/2.spider/美团/深圳烤肉1.csv',
                names = ['店铺名称', '店铺地址', '人均消费', '店铺评分', '评论人数', '所在商圈', '图片链接','店铺类型','联系方式'])
df.sample(5)
店铺名称 店铺地址 人均消费 店铺评分 评论人数 所在商圈 图片链接 店铺类型 联系方式
19254 一品屋烧烤(罗湖店) 罗湖区南湖街道春风路罗湖村93号一楼105号铺(罗湖村综合楼) 62.0 3.9 31 火车站 http://p1.meituan.net/w.h/mogu/b35f515f50f9b06... 烤串 18128808065
10046 卓弘烧烤 龙岗区黄宝路66-18-4号 0.0 3.5 0 龙岗中心城 http://qcloud.dpfile.com/pc/mTTAdA3HDYDZ5dLaVg... 烤串 NaN
322 羊妈妈(下沙二店) 福田区下沙街16-2号 0.0 0.0 0 上沙/下沙 http://p0.meituan.net/w.h/deal/__22067385__260... 融合烤肉 NaN
6316 南里奥巴西烤肉(同泰广场店) 宝安区蚝业路永福路106号同泰时代中心1楼 75.0 3.0 410 宝安机场 http://p1.meituan.net/w.h/bbia/ff06a7746ec5968... 自助烧烤 0755-23505770/23505790
21677 牛炙烤肉(南山店) 南山区南山大道2017-7号 67.0 4.4 334 南山中心区 https://img.meituan.net/w.h/msmerchant/41b40ab... 融合烤肉 17704062830

查看数据类型

用Info方法查看各字段数据类型,符合预期,无需转换。

df.info()

RangeIndex: 21925 entries, 0 to 21924
Data columns (total 9 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   店铺名称    21925 non-null  object 
 1   店铺地址    21925 non-null  object 
 2   人均消费    21925 non-null  float64
 3   店铺评分    21925 non-null  float64
 4   评论人数    21925 non-null  int64  
 5   所在商圈    21925 non-null  object 
 6   图片链接    21925 non-null  object 
 7   店铺类型    21925 non-null  object 
 8   联系方式    19154 non-null  object 
dtypes: float64(2), int64(1), object(6)
memory usage: 1.5+ MB

删除重复数据

df = df.drop_duplicates() 

缺失值处理

由上文可知,仅联系方式字段含有缺失值,用文本填充。

df = df.fillna('暂无数据')

店铺地址清洗

通过店铺地址字段截取所属区县,另外,“南澳大”属于龙岗区,直接用replace替换。

df['所属区县'] = df['店铺地址'].str[:3].str.replace('南澳大','龙岗区')

店铺评分清洗

根据美团评分方法,对店铺评分字段进行切分,获得评分类型列。

cut = lambda x : '一般' if x <= 3.5 else ('不错' if x <= 4.0 else('好' if x <= 4.5 else '很好'))
df['评分类型'] = df['店铺评分'].map(cut)

描述性统计


查看基本统计量

df.describe() 
人均消费 店铺评分 评论人数
count 4141.000000 4141.000000 4141.000000
mean 29.714079 2.084835 140.367061
std 59.889410 2.002979 863.245771
min 0.000000 0.000000 0.000000
25% 0.000000 0.000000 0.000000
50% 0.000000 3.000000 0.000000
75% 58.000000 3.700000 12.000000
max 1742.000000 5.000000 25786.000000

计算相关系数

df.corr()
人均消费 店铺评分 评论人数
人均消费 1.000000 0.494744 0.106694
店铺评分 0.494744 1.000000 0.168090
评论人数 0.106694 0.168090 1.000000

绘制回归图

import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置加载的字体名
plt.rcParams['axes.unicode_minus'] = False   # 解决保存图像是负号'-'显示为方块的问题 
fig,axes=plt.subplots(2,1,figsize=(12,12)) 
sns.regplot(x='人均消费',y='店铺评分',data=df,color='r',marker='+',ax=axes[0])
sns.regplot(x='评论人数',y='店铺评分',data=df,color='g',marker='*',ax=axes[1])

用Python爬取美团烤肉店数据并分析_第5张图片
通过绘制回归图,我们发现人均消费与店铺评分具有正相关,评论人数和店铺评分具有正相关。这与我们的常识也较为接近。

数据分析

本文数据可视化主要用到pyecharts库,它能轻松实现酷炫的图表效果。如果您对可视化感兴趣,可查看J哥往期原创文章「数据可视化分析系列」,涉及地产、电商、招聘等各领域。

地区分布

深圳烤肉店主要分布在龙岗区、龙华区、南山区和福田区,盐田区和坪山区烤肉店较少。烤肉店的选址一个重要因素就是人流量,龙岗区和龙华区为深圳主要的生活居住区,而南山区和福田区为深圳的核心商业聚集地,巨大的需求为烤肉店的布局奠定了基础。

from pyecharts.charts import *
from pyecharts import options as opts
from pyecharts.globals import ThemeType  #引入主题
df1 = df.groupby('所属区县')['店铺名称'].count() #按所属区县分组,对店铺名称计数
df1 = df1.sort_values(ascending=False) #降序
regions = df1.index.to_list()
values = df1.to_list()
c = (
    Map(init_opts=opts.InitOpts(theme = ThemeType.WONDERLAND)) #PURPLE_PASSION
    .add(
        "", 
        zip(regions, values), 
        maptype="深圳"
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title="深圳烤肉店分布",subtitle="数据来源:美团",pos_top="-1%", pos_left = 'center' ),
        visualmap_opts=opts.VisualMapOpts(max_=1000)
    )
    )
c.render_notebook()

用Python爬取美团烤肉店数据并分析_第6张图片

所在商圈

仅仅知道烤肉店行政区分布,对于烤肉店选址作用其实不大。于是,我们进一步细化到商圈,看看哪些商圈的烤肉店较多。在深圳所有商圈中,龙华区的民治和龙华、光明区的公明烤肉店数量都超过了150家。

df2 = df.groupby('所在商圈')['店铺名称'].count()
df2 = df2.sort_values(ascending=True)[-10:]
df2 = df2.round(2)
c = (
    Bar(init_opts=opts.InitOpts(theme=ThemeType.WONDERLAND))
    .add_xaxis(df2.index.to_list())
    .add_yaxis("",df2.to_list()).reversal_axis() #X轴与y轴调换顺序
    .set_global_opts(title_opts=opts.TitleOpts(title="商圈烤肉店数量top10",subtitle="数据来源:美团",pos_left = 'center'),
                       xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=16)), #更改横坐标字体大小
                       yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=16)), #更改纵坐标字体大小
                       )
    .set_series_opts(label_opts=opts.LabelOpts(font_size=16,position='right'))
    )
c.render_notebook()

用Python爬取美团烤肉店数据并分析_第7张图片

评分排行

烤肉店的评分在一定程度上反映了消费者对烤肉店的态度和看法。通过计算各个行政区烤肉店平均评分,我们发现,深圳烤肉店普遍评分不高,都在3分以下,且各地区评分差异不大。

df3 = df.groupby('所属区县')['店铺评分'].mean()
df3 = df3.sort_values(ascending=False) 
df3 = df3.round(2)
c = (
    Bar(init_opts=opts.InitOpts(theme=ThemeType.WONDERLAND))
    .add_xaxis(df3.index.to_list())
    .add_yaxis("",df3.to_list())
    .set_global_opts(title_opts=opts.TitleOpts(title="各地区平均评分",subtitle="数据来源:美团",pos_left = 'center'),
                       xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=16)), #更改横坐标字体大小
                       yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=16)), #更改纵坐标字体大小
                       )
    .set_series_opts(label_opts=opts.LabelOpts(font_size=16))
    )
c.render_notebook()

用Python爬取美团烤肉店数据并分析_第8张图片

评分类型

根据不同评分类型绘制饼图,我们发现深圳评分为“一般”的烤肉店数量占比高达73.9%。评分类型为“不错”的烤肉店仅占6.52%。烤肉店较低的评分意味着,作为市场的进入者,如果新开烤肉店能够提供较好的质量和服务,且获得消费者好评,将比较容易在众多烤肉店中脱颖而出。

df4 = df.groupby('评分类型')['店铺名称'].count()
df4 = df4.sort_values(ascending=False) 
regions = df4.index.to_list()
values = df4.to_list()
c = (
        Pie(init_opts=opts.InitOpts(theme=ThemeType.WONDERLAND))
        .add("", zip(regions,values))
        .set_global_opts(title_opts=opts.TitleOpts(title="不同评分类型店铺数量",subtitle="数据来源:美团",pos_top="-1%",pos_left = 'center'))
        .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:{d}%",font_size=18))
    )
c.render_notebook()

用Python爬取美团烤肉店数据并分析_第9张图片
我们继续将评分类型分析细化到深圳的各个行政区,罗湖区评分为“一般”的烤肉店占比相对低一些。其他地区占比都超过了一半。这进一步反映了深圳烤肉店评分的整体情况,排除了某个或某几个行政区评分异常值的影响。

h = pd.pivot_table(df,index=['评分类型'],values=['店铺名称'],
               columns=['所属区县'],aggfunc=['count'])
k = h.droplevel([0,1],axis=1)  #删除指定的索引/列级别
c = (
    Polar(init_opts=opts.InitOpts(theme=ThemeType.WONDERLAND))
    .add_schema(angleaxis_opts=opts.AngleAxisOpts(data=k.columns.tolist(), type_="category"))
    .add("一般",h.values.tolist()[0], type_="bar", stack="stack0")
    .add("不错",h.values.tolist()[1], type_="bar", stack="stack0")
    .add("好", h.values.tolist()[2], type_="bar", stack="stack0")
    .add("很好", h.values.tolist()[3], type_="bar", stack="stack0")
    .set_global_opts(title_opts=opts.TitleOpts(title="不同地区评分情况",subtitle="数据来源:美团"))

)
c.render_notebook()

用Python爬取美团烤肉店数据并分析_第10张图片

人均消费

从深圳各行政区烤肉店人均消费来看,南山区和福田区人均消费较高,坪山区和光明区人均消费较低。在消费量一致的假设下,人均消费的多少取决于烤肉的价格。南山区和福田区高昂的开店成本以及消费者较强的消费能力,是烤肉人均消费较高的重要动因。

df5 = df.groupby('所属区县')['人均消费'].mean()
df5 = df5.sort_values(ascending=True)
df5 = df5.round(2)
c = (
    Bar(init_opts=opts.InitOpts(theme=ThemeType.WONDERLAND))
    .add_xaxis(df5.index.to_list())
    .add_yaxis("",df5.to_list()).reversal_axis() #X轴与y轴调换顺序
    .set_global_opts(title_opts=opts.TitleOpts(title="各地区人均消费",subtitle="数据来源:美团",pos_left = 'center'),
                       xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=16)), #更改横坐标字体大小
                       yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=16)), #更改纵坐标字体大小
                       )
    .set_series_opts(label_opts=opts.LabelOpts(font_size=16,position='right'))
    )
c.render_notebook()

用Python爬取美团烤肉店数据并分析_第11张图片

由上图可知,深圳各行政区烤肉人均消费普遍低于50元,那是不是意味着如果要开烤肉店的话,定价不能太高。为了回答这个问题,我们可以筛选出人均消费大于1000元的烤肉店,看下消费者的评价情况。由下表可知,即使三家烤肉店定价很高,仍然获得了消费者较高的评价。因此,烤肉的定价还需根据你的市场定位来,如果定位高端人群,那么较高的价格消费者也是可以接受的。

df_1 = df[df['人均消费']>1000]
df_1[['店铺名称','人均消费','评分类型','所在商圈']]
店铺名称 人均消费 评分类型 所在商圈
106 和牛食验室WagyuLab 1049.0 很好 会展中心
3434 茑山·怀石炭火烧 1104.0 很好 华侨城
3466 源烧肉 mihara 1742.0 不错 西丽

店铺类型

从深圳烤肉店店铺类型来看,烤串、烧烤和融合烤肉最多,韩式烤肉、日式烤肉等店铺相对更少一些。

df6 = df.groupby('店铺类型')['店铺名称'].count()
df6 = df6.sort_values(ascending=False)[:10]
df6 = df6.round(2)
regions = df6.index.to_list()
values = df6.to_list()
c = (
        Pie(init_opts=opts.InitOpts(theme=ThemeType.WONDERLAND))
        .add("", zip(regions,values),radius=["40%", "75%"])
        .set_global_opts(title_opts=opts.TitleOpts(title="不同店铺类型店铺数量",pos_top="-1%",pos_left = 'center'))
        .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}",font_size=18))
    )
c.render_notebook()

用Python爬取美团烤肉店数据并分析_第12张图片

从评分来看,串串香、牛排和怀石料理的烤肉评分较高。另外,日式自助烤肉评分也排到了前十名,日式烤肉对肉要求比较高,日式肉类也会稍微腌制,但是总体以体现肉的鲜美为主。精致的日式烤肉,博得了众多深圳消费者的青睐。

df6 = df.groupby('店铺类型')['店铺评分'].mean()
df6 = df6.sort_values(ascending=True)
df6 = df6.round(2)
df6 = df6.tail(10)
c = (
    Bar(init_opts=opts.InitOpts(theme=ThemeType.WONDERLAND))
    .add_xaxis(df6.index.to_list())
    .add_yaxis("",df6.to_list()).reversal_axis() #X轴与y轴调换顺序
    .set_global_opts(title_opts=opts.TitleOpts(title="不同店铺类型评分",subtitle="数据来源:美团",pos_left = 'center'),
                       xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=16)), #更改横坐标字体大小
                       yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=16)), #更改纵坐标字体大小
                       )
    .set_series_opts(label_opts=opts.LabelOpts(font_size=16,position='right'))
    )
c.render_notebook()

用Python爬取美团烤肉店数据并分析_第13张图片

评论人数

从评论人数来看,综合自助和韩式烤肉店评论人数均在10万左右,而评论人数在一定程度上反映了烤肉店的热度。不同的综合自助烤肉店一般价格和肉质差异较大,获得较多的评论也不足为奇。而韩式烤肉通常会对肉类进行腌制,口感偏重,也被深圳消费者广泛讨论。

df7 = df.groupby('店铺类型')['评论人数'].sum()
df7 = df7.sort_values(ascending=True)
df7 = df7.tail(10)
c = (
    Bar(init_opts=opts.InitOpts(theme=ThemeType.WONDERLAND))
    .add_xaxis(df7.index.to_list())
    .add_yaxis("",df7.to_list()).reversal_axis() #X轴与y轴调换顺序
    .set_global_opts(title_opts=opts.TitleOpts(title="不同店铺类型评论人数",subtitle="数据来源:美团",pos_left = 'center'),
                       xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=16)), #更改横坐标字体大小
                       yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=16)), #更改纵坐标字体大小
                       )
    .set_series_opts(label_opts=opts.LabelOpts(font_size=16,position='right'))
    )
c.render_notebook()

用Python爬取美团烤肉店数据并分析_第14张图片

店铺取名

当然,开烤肉店除了要了解消费者的偏好以及竞争对手的优劣势,还一个重要步骤就是给自己开的烤肉店取名了。一个响亮的烤肉店名字,能够给消费者留下较深的记忆度,同时也能带来品牌效应。于是,J哥对深圳所有烤肉店名进行分词并绘制了词云图,发现除了烧烤、烤肉等字样,词频较高的还有音乐、木屋和炭火等。差异化市场定位,给烤肉搭配多样化的元素,在店名中凸显出来,不失为一个不错的选择。

import jieba
import stylecloud
from IPython.display import Image 
# 定义分词函数
def get_cut_words(content_series):
    # 读入停用词表
    stop_words = [] 
    
    with open("./stop_words.txt", 'r', encoding='utf-8') as f:
        lines = f.readlines()
        for line in lines:
            stop_words.append(line.strip())

    # 添加关键词
    my_words = ['', '']  
    
    for i in my_words:
        jieba.add_word(i) 

    # 自定义停用词
    my_stop_words = ['东北', '福田','公园','车公庙','梅林','购物']   
    stop_words.extend(my_stop_words)               

    # 分词
    word_num = jieba.lcut(content_series.str.cat(sep='。'), cut_all=False)

    # 条件筛选
    word_num_selected = [i for i in word_num if i not in stop_words and len(i)>=2]
    
    return word_num_selected

# 绘制词云图
text1 = get_cut_words(content_series=df['店铺名称'])
stylecloud.gen_stylecloud(text=' '.join(text1), max_words=1000,
                          collocations=False,
                          font_path='字酷堂清楷体.ttf',
                          icon_name='fas fa-shopping-bag',
                          size=653,
                          output_name='./烤肉.png')
Image(filename='./烤肉.png') 

用Python爬取美团烤肉店数据并分析_第15张图片


更多精彩内容,欢迎关注J哥微信公众号「菜J学Python」。
用Python爬取美团烤肉店数据并分析_第16张图片

你可能感兴趣的:(用Python爬取美团烤肉店数据并分析)