爬取美團1024家烤肉店資料,看看為什麼那麼多人喜歡吃,祕訣在哪
前言
美國作家杜魯門·卡波特曾說:“夢是心靈的思想,是我們的祕密真情。”在J哥的內心深處,也曾有一個小小的夢想,那就是開一家烤肉店。幾串烤肉、一杯美酒,即可享受深夜路邊的自由得意與平凡熱辣的市井人生。
可是,想開烤肉店可沒那麼容易,首先你得了解市場。於是,打開了美團,一頓操作爬取了深圳所有的烤肉店資料,然後清洗資料並做視覺化分析,試圖摸到一點開烤肉店的門道。
PS:如有需要Python學習資料的小夥伴可以加下方的群去找免費管理員領取
可以免費領取原始碼、專案實戰視訊、PDF檔案等
資料獲取
美團網很明顯是動態網頁,需要通過解析介面或用Selenium爬取
美團網URL:
https://sz.meituan.com/
分析真實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
主要引數:
- 30:城市id(30代表深圳)
- limit:每頁店鋪數量
- offset:翻頁引數(每增加32翻頁一次)
- q:關鍵字(本例為烤肉)
按上述介面爬取只能獲得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萬多個烤肉店資訊,為了方便視覺化分析,還需要對爬取的資料進行簡單清洗。
匯入資料
匯入資料並新增列名,用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)
檢視資料型別
用Info()方法檢視各欄位資料型別,符合預期,無需轉換。
df.info()
刪除重複資料
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()
計算相關係數
df.corr()
繪製迴歸圖
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])
通過繪製迴歸圖,我們發現人均消費與店鋪評分具有正相關,評論人數和店鋪評分具有正相關。這與我們的常識也較為接近。
資料分析
本文資料視覺化主要用到pyecharts庫,它能輕鬆實現酷炫的圖表效果。
地區分佈
深圳烤肉店主要分佈在龍崗區、龍華區、南山區和福田區,鹽田區和坪山區烤肉店較少。烤肉店的選址一個重要因素就是人流量,龍崗區和龍華區為深圳主要的生活居住區,而南山區和福田區為深圳的核心商業聚集地,巨大的需求為烤肉店的佈局奠定了基礎。
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()
所在商圈
僅僅知道烤肉店行政區分佈,對於烤肉店選址作用其實不大。於是,我們進一步細化到商圈,看看哪些商圈的烤肉店較多。在深圳所有商圈中,龍華區的民治和龍華、光明區的公明烤肉店數量都超過了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()
評分排行
烤肉店的評分在一定程度上反映了消費者對烤肉店的態度和看法。通過計算各個行政區烤肉店平均評分,我們發現,深圳烤肉店普遍評分不高,都在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()
評分型別
根據不同評分型別繪製餅圖,我們發現深圳評分為“一般”的烤肉店數量佔比高達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()
我們繼續將評分型別分析細化到深圳的各個行政區,羅湖區評分為“一般”的烤肉店佔比相對低一些。其他地區佔比都超過了一半。這進一步反映了深圳烤肉店評分的整體情況,排除了某個或某幾個行政區評分異常值的影響。
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()
人均消費
從深圳各行政區烤肉店人均消費來看,南山區和福田區人均消費較高,坪山區和光明區人均消費較低。在消費量一致的假設下,人均消費的多少取決於烤肉的價格。南山區和福田區高昂的開店成本以及消費者較強的消費能力,是烤肉人均消費較高的重要動因。
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()
由上圖可知,深圳各行政區烤肉人均消費普遍低於50元,那是不是意味著如果要開烤肉店的話,定價不能太高。為此,我們可以篩選出人均消費大於1000元的烤肉店,看下消費者的評價情況。由下表可知,雖然三家烤肉店定價很高,卻獲得了消費者較高的評價。因此,烤肉的定價還需根據你的市場定位來,如果定位高階人群,那麼較高的價格消費者也是可以接受的。
df_1 = df[df['人均消費']>1000] df_1[['店鋪名稱','人均消費','評分型別','所在商圈']]
店鋪型別
從深圳烤肉店店鋪型別來看,烤串、燒烤和融合烤肉最多,韓式烤肉、日式烤肉等店鋪相對更少一些。
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()
從評分來看,串串香、牛排和懷石料理的烤肉評分較高。另外,日式自助烤肉評分也排到了前十名,日式烤肉對肉要求比較高,日式肉類也會稍微醃製,但是總體以體現肉的鮮美為主。精緻的日式烤肉,博得了眾多深圳消費者的青睞。
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()
評論人數
從評論人數來看,綜合自助和韓式烤肉店評論人數均在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()
店鋪取名
當然,開烤肉店除了要了解消費者的偏好以及競爭對手的優劣勢,還一個重要步驟就是給自己開的烤肉店取名了。一個響亮的烤肉店名字,能夠給消費者留下較深的記憶度,同時也能帶來品牌效應。於是,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')
本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯絡我們以作處理。
以上文章來源於菜J學Python ,作者J哥