数据获取流程

  1. 首先选择相应的网站,我选择途牛网上获取与全国景点分布有关的信息,分析网页。定位我需要的数据,我发现景区名称和景区分属的地区分别处于网页的h3内a标签内,根据标签的属性我可以定位到它们的具体位置。以同样的方法分析网页源代码,可以找到游客满意度,游客点评数和门票价格的与景点相关的信息,使用python的requets库和beautifulsoup库抓取来自途牛网的全部旅行信息,然后将数据保存为dataframe形式,然后使用dataframe的to_csv()方法将所抓取的数据以.csv的格式保存。



    写入的csv文件的数据的基本样式为:

出行数据可视化 旅游数据可视化_饼图


1.1 爬取数据加写出csv格式数据代码:

from bs4 import BeautifulSoup
import pandas as pd
import numpy as np
import requests
import time
from lxml import etree
import bs4
def getHtml(url):
    try:
        r=requests.get(url)
        r.raise_for_status()
        r.encoding=r.apparent_encoding
        return r.text
    except:
        return "读取失败"
def get_data(u):
   r=getHtml(u)
   soup=BeautifulSoup(r,"lxml")
   data=soup.find('ul',class_='list_view').find_all('li',class_="list_item")
   datafor=[]
   for i in data:
       dicts={}
       dicts['景点名称']=(i.find('h3').find('a',target="_blank")).text
       dicts["景区所在地"]=((i.find('h3').find('span', class_="park_city")).find('a',rel="nofollow")).text
       dicts["游客满意度(百分制)"]=(i.find('p',class_="ticket").find('strong',class_="t_ticket")).text
       dicts["游客点评数"]=(i.find('p',class_="ticket").find('strong',class_="dp f_f60")).text
       dicts['门票价格']=(i.find('div',class_="attri_price").find('span',class_='price f_yh').find('em')).text
       datafor.append(dicts)
   return datafor
def main():
    data=[]
    for i in range(1,288):
        try:
            data.extend(get_data("https://menpiao.tuniu.com/cat_0_0_0_0_0_0_1_1_"+str(i)+'.html'))
            time.sleep(0.3)#防止爬取速度过快
        except:
            continue
    p=pd.DataFrame(data)
    p.to_csv(r"C:/Users/gcc/Desktop/quanguo.csv", encoding="utf-8",index=False)
main()
  1. 通过对全国旅游景点的分析,可以将视线固定在某一个综合评价最好的省份,然后选择在去哪儿网,获取某个省份的景点分布有关的信息。通过分析去哪儿网的网页源代码,和1中分析相似,可以观察出我需要的数据都存储在固定的标签种类下,如图上所示,使用与1中相似的方法进行爬取数据并保存,保存的基本样式与1中相似。
    获得的该省份的数据格式如下:
  2. 再通过对某一省份的相关分析之后,可以将目标集中在其中的几个市,因此我还是选择在去哪儿网站上爬取与之相关的几个市的信息,爬取这个市的各个旅游景区级别,也保存在.csv文件中,可视化分析这几个市的关于著名景区个数的相关信息,就可以得出一些基本结论。
    某个省份需要爬取的景区数据网页源代码格式如下:

    5.最后我想对目标城市的餐饮销量第一的具体门店进行分析,获取该门店的用户相关点评信息,但是由于我还是python新手,用户点评信息有反爬措施,所以我选择用第三方软件“后裔采集器”,对几个销量第一门店的相关数据进行爬取,并制作相关词云图。

数据清理和可视化

  1. 首先,针对从途牛网获取的全国旅游景点信息quanguo.csv进行数据清洗,去掉数据中景区所在地非中国的数据,如“亚洲”,然后使用strip()函数去掉其中的百分号,方便后续的比较。
    将数据清理之后,要导入numpy,pandas,matplotlib和pyecharts库,对数据进行可视化处理。先使用matplotlib绘制全国各省景点个数统计柱状图,如图1.1所示。使用pyecharts中的Pie绘制相应饼图。pyecharts是基于web页面进行图像的展示,图片展示形式是.html文件,可以用浏览器下载并保存在本地。绘制完后的饼图如图1.2所示
    图1.1

图1.2

出行数据可视化 旅游数据可视化_html_02


分析饼图和柱状图信息,

得出结论:广东省,江苏省,浙江省的旅游景点的个数排名前三。其中江苏省,浙江省都毗邻安徽省,在我们的选择范围内。

之后,我想描述省份和该省份的游客满意度大于94%的景点个数和游客的满意度这三个变量之间的关系,我选择用matlplotlib绘制气泡图,横坐标表示省份,纵坐标表示该省份的景点数量,气泡的大小与游客满意度的大小有关,绘制相应气泡图,如图1.3所示 图1.3


出行数据可视化 旅游数据可视化_出行数据可视化_03


绘制条图,饼图,气泡图的代码如下:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pyecharts import Pie
plt.rcParams['font.sans-serif'] = ['SimHei'] # 在matplotlib中设置字体为SimHei黑体
plt.rcParams['axes.unicode_minus'] = False# 解决matplotlib坐标轴负号‘-’显示为方块问题
df = pd.read_csv(r"C:\Users\gcc\Desktop\quanguo2.csv")
def drawbar():#条图
    lis=[]
    lis=list(df['景区所在地'])
    dic={}
    for i in lis:
        dic[i]=dic.get(i,0)+1
    print(dic)
    key=dic.keys()
    value=dic.values()
    plt.figure(figsize=(10,5))
    plt.bar(key,value)
    plt.xticks(rotation=-45)
    plt.xlabel("省份")
    plt.ylabel("景点个数")
    plt.title("全国景点个数统计表")
    plt.tight_layout()#F防止标签显示不全
    for x,y in enumerate(value):
      plt.text(x-0.25,y+0.1,'%s'%int(y))
    plt.show()
def drawpie():#饼图
    lis = []
    lis = list(df['景区所在地'])
    dic = {}
    for i in lis:
        dic[i] = dic.get(i, 0) + 1
    print(dic)
    key = dic.keys()
    value = dic.values()
    pie=Pie("全国景点分布",title_pos='center',width=800,height=500)
    pie.add("",key,value,is_label_show=True,is_more_utils=True,center=['52%','50%'],is_legend_show=True,legend_top="6%",legend_pos="left",legend_orient="vertical")
    pie.render(path="cpie.html")
def drawqipao():#气泡图
    a=df[df['游客满意度(百分制)']>94]
    a=a.loc[:,['景区所在地','游客满意度(百分制)','游客点评数']]
    b=a.groupby(a['景区所在地']).count()
    c=a['游客点评数'].groupby(a['景区所在地']).sum()
    x=list(c.index)#设置x
    y=list(b['游客满意度(百分制)'])#设置y
    size=list((c.values.astype(np.int))/100)#设置气泡大小和好评数有关
    color=np.random.rand(len(x))
    plt.figure(figsize=(10,5))
    plt.scatter(x,y,s=size,c=color,alpha=0.6)
    plt.xticks(rotation=45)
    for i in range(len(x)):
      plt.text(x[i],y[i],x[i],color='r',rotation=15)
    plt.xlabel("省份")
    plt.ylabel('好评率>95景点个数')
    plt.tight_layout()
    plt.show()
    print(size)
drawpie()

分析图1.3得出结论:江苏省不仅仅是游客满意度大于94%的景点个数最多的省份,同时气泡也较大,说明游客满意度较高,浙江虽然景点众多,但是游客满意度大于94的景点却不如江苏,因此我们可以将旅游省份确定为江苏。
2. 将旅游景点确定为江苏之后,就要对江苏具体的各级市的旅游景点相关信息进行分析。将从去哪儿网获取的江苏省旅游景点信息jiangsu.csv,进行数据清醒,没有级别的景区星级缺省为0,筛选所需要的数据,首先使用pyecharts绘制江苏省各市景区个数环状饼图,如图2.1所示,再使用更直观的方式,使用pyecharts绘制江苏省各市景区分布排名前十的漏斗图,如图2.2所示。

图2.1


出行数据可视化 旅游数据可视化_数据_04

图2.2


出行数据可视化 旅游数据可视化_数据_05


江苏景点分布图和景点漏斗图代码如下:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pyecharts import Pie,Funnel,Boxplot,Map
plt.rcParams['font.sans-serif'] = ['SimHei'] # 在matplotlib中设置字体为SimHei黑体
plt.rcParams['axes.unicode_minus'] = False# 解决matplotlib坐标轴负号‘-’显示为方块问题
df = pd.read_csv(r"C:\Users\gcc\Desktop\jiangsu.csv")
def drawpie():#绘制江苏景点分布饼图
    lis = []
    lis = list(df['城市'])
    dic = {}
    for i in lis:
        dic[i] = dic.get(i, 0) + 1
    print(dic)
    key = dic.keys()
    value = dic.values()
    pie = Pie("江苏景点分布图",title_pos="center")
    pie.add("", key, value, is_label_show=True, is_more_utils=True, center=['50%', '60%'], is_legend_show=True,legend_top="8%",radius=["30%","75%"])
    pie.render(path="pie.html")
def drawFunnel():#绘制江苏景区分布漏斗图
    lis = []
    lis = list(df['城市'])
    dic = {}
    for i in lis:
        dic[i] = dic.get(i, 0) + 1
    dic=dict(sorted(dic.items(),key=lambda X:X[1],reverse=True))
    key=list(dic.keys())
    value=list(dic.values())
    pie=Funnel("景点数量漏斗图",title_pos="center")
    pie.add("",key[:10],value[:10],is_label_show=True,label_pos="inside",legend_top="6%")
    pie.render(path="funnel.html")
    print(dic)

分析图2.1,2.2可以非常直观的看出江苏省各个市景点所占的比例,排名前十的市,可以看到南京,苏州,常州,无锡,连云港分别占前五名。
接着,我准备分析景区的热度和该景区的级别之间的关系,还有景区的门票销量和景区的级别之间的关系,数据中的景区星级只有5A,4A,3A和0A四种。所以我使用pyecharts绘制景区热度箱型图和景区销量箱型图,如图2.3,2.4所示

图2.3


出行数据可视化 旅游数据可视化_饼图_06

图2.4


出行数据可视化 旅游数据可视化_出行数据可视化_07


景区热度和销量的箱型图代码:

def drawtbox():#h绘制景区热度的箱型图
    df['热度']=df['热度'].astype(np.float)
    dic=df.loc[:,['星级','热度']]
    data=dic['热度'].values
    index=dic['星级'].values
    se=pd.Series(data=data,index=index)
    list5=se['5A景区'].tolist()
    list4=se['4A景区'].tolist()
    list3=se['3A景区'].tolist()
    list0=se['0'].tolist()
    print(list0)
    box=Boxplot('景区热度箱型图')
    x=['5A景区','4A景区','3A景区','其他景区']
    y=[]#存放5A,4A,3A,其他的热度
    y.append(list5)
    y.append(list4)
    y.append(list3)
    y.append(list0)
    _y=box.prepare_data(y)#转换数据
    box.add('热度',x,_y,item_color='green',xaxis_line_color='blue',yaxis_line_color='blue')
    box.render(path='box.html')
def drawboxs():#绘制景区销量箱型图
    df['销量'] = df['销量'].astype(np.float)
    dic = df.loc[:, ['星级', '销量']]
    data = dic['销量'].values
    index = dic['星级'].values
    se = pd.Series(data=data, index=index)
    list5 = se['5A景区'].tolist()
    list4 = se['4A景区'].tolist()
    list3 = se['3A景区'].tolist()
    list0 = se['0'].tolist()
    print(list0)
    box = Boxplot('景区销量箱型图')
    x = ['5A景区', '4A景区', '3A景区', '其他景区']
    y = []  # 存放5A,4A,3A,其他的热度s
    y.append(list5)
    y.append(list4)
    y.append(list3)
    y.append(list0)
    _y = box.prepare_data(y)  # 转换数据
    box.add('销量', x, _y, item_color='green', xaxis_line_color='blue', yaxis_line_color='blue')
    box.render(path='boxs.html')

由于受到疫情的影响,从网站上爬取的景点热度和月销量信息可能与正常时候的数据有些许差异,但是总体上来看,在景区热度箱型图中,可以看到5A景区的热度的中位数是0.78,还是有许多热度大于0.8的景区,4A景区的热度的中位数为0.74,但是它的大部分景区热度都低于0.7,3A景区的热度中位数是0.7,绝大部分都低于中位数,其他景区的热度几乎为0.
在景区销量的箱型图中,我们可以看到景区门票的销量收疫情影响都比较低,但是总体都是随着星级的减少而递减的。
因此,可以得到结论:景区星级和景区热度,景区星级和景区门票销量中存在正相关关系,因此,想去热度高,销量好的“网红打卡景点”我们只需要统计江苏省各级市5A,4A景区个数即可。
最后,统计江苏省各市的5A,4A景点的个数,安装省市地图的python包,使用pyecharts的Map绘制江苏省著名景点分布热力图,如图2.5所示。

图2.5


出行数据可视化 旅游数据可视化_饼图_08


(绘制地图需要安装地图文件包:

全球国家地图: echarts-countries-pypkg

中国省级地图: echarts-china-provinces-pypkg

中国市级地图: echarts-china-cities-pypkg

中国县区级地图: echarts-china-counties-pypkg

中国区域地图: echarts-china-misc-pypkg)

绘制热力图的代码:

def drawmap():#统计江苏省的5,4星级的景点个数热力图
    map = Map("江苏5A,4A级景点热力图",'江苏', width=1200, height=600)
    city = ['南京市', '苏州市', '常州市', '无锡市', '连云港市', '徐州市', '南通市', '淮安市', '盐城市', '扬州市', '镇江市', '泰州市', '宿迁市']
    dic = df.loc[:, ['星级', '城市']]
    data = dic['星级'].values
    index = dic['城市'].values
    se = pd.Series(data=data, index=index)
    dic={}
    list1=se['南京'].tolist()
    list2=se['苏州'].tolist()
    list3 = se['常州'].tolist()
    list4 = se['无锡'].tolist()
    list5 = se['连云港'].tolist()
    list6 = se['徐州'].tolist()
    list7 = se['南通'].tolist()
    list8 = list(se['淮安'])
    list9 = se['盐城'].tolist()
    list10 = se['扬州'].tolist()
    list11= se['镇江'].tolist()
    list12 = se['泰州'].tolist()
    list13= se['宿迁'].tolist()
    dic['南京']=sumup(list1,0)
    dic['苏州'] = sumup(list2, 0)
    dic['常州'] = sumup(list3, 0)
    dic['无锡'] = sumup(list4, 0)
    dic['连云港'] = sumup(list5, 0)
    dic['徐州'] = sumup(list6, 0)
    dic['南通'] = sumup(list7, 0)
    dic['淮安'] = sumup(list8, 0)
    dic['盐城'] = sumup(list9, 0)
    dic['扬州'] = sumup(list10, 0)
    dic['镇江'] = sumup(list11, 0)
    dic['泰州'] = sumup(list12, 0)
    dic['宿迁'] = sumup(list13, 0)
    #print(dic)
    values = list(dic.values())
    map.add('江苏', city, values, visual_range=[1, 14], maptype='江苏', is_visualmap=True, visual_text_color='#000',is_label_show=True)
    map.render(path="hotpicture.html")

分析2.5数据我们可以得到结论,5A,4A景区分布最多的前三个市为苏州,南京,和无锡。在对比前面得到的景点个数最多的前五个市为:南京,苏州,常州,无锡,连云港。若是对景区星级要求较高,苏州,南京,无锡是较好的选择,如果对景区星级不高,南京不为是一个好选择,因为它的著名景区分布也较高,同时全市的景区个数 也最多。
3. 最后对著名景区分布最多的前三个省份:南京,苏州,无锡,进行餐饮方面的分析。我选择从马蜂窝网获取这三个市的餐饮门店,餐饮销量的信息,按照餐饮销量排名,对比三个城市的餐饮门店销量排名的前五名,同时得到南京餐饮销量第一:夫子庙美食街,苏州餐饮销量第一:哑巴生煎,无锡餐饮销量第一:三凤桥·三风酒家,然后使用matplotlib绘制三个城市销量排名前五餐饮的对比并列柱状图,如图3.1所示。

图3.1


出行数据可视化 旅游数据可视化_出行数据可视化_09


分析图3.1可以得到结论,南京餐饮的销量最大,苏州次之,无锡最低,因此南京的小吃应该是最多的。

用后裔采集器采集了评价相关信息,使用matplotlib绘制与图3.1类型的评价等级并列条图,对比评价等级 从5-1的评价数,如图3.2所示。 图3.2

出行数据可视化 旅游数据可视化_出行数据可视化_10


分析图3.2得出结论:南京的小吃最多,餐饮店的的销量最高,同时南京的餐饮好评率最高,大部分是五星,四星,三星,非常适合对食物比较感兴趣的旅游人群,苏州也餐饮评价也较高,无锡较低。

绘制餐饮门店排名并列条图的代码:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 在matplotlib中设置字体为SimHei黑体
plt.rcParams['axes.unicode_minus'] = False# 解决matplotlib坐标轴负号‘-’显示为方块问题
df1=pd.read_csv(r"C:\Users\gcc\Desktop\nanjingf.csv").drop_duplicates()#爬取的数据可能有重复行
df2=pd.read_csv(r"C:\Users\gcc\Desktop\suzhouf.csv").drop_duplicates()
df3= pd.read_csv(r"C:\Users\gcc\Desktop\wuxif.csv").drop_duplicates()

def creatSeries(dic):
    index=dic['门店名称'].values
    data=dic['评论数'].values
    se=pd.Series(data=data,index=index)
    return se
def morebar(se1,se2,se3):#绘制三大城市的餐饮销量并列条图
    plt.figure(figsize=(20,10))
    x=np.arange(5)
    y1=se1.values.astype(np.int)
    y2=se2.values.astype(np.int)
    y3=se3.values.astype(np.int)
    tick_label=['top1','top2','top3','top4','top5']
    bar_width=0.3
    plt.bar(x,y1,bar_width,color='salmon',align='center',label='南京')
    plt.bar(x+bar_width, y2, bar_width, color='orchid', align='center', label='苏州')
    plt.bar(x+2*bar_width, y3, bar_width, color='orange', align='center', label='无锡')
    plt.legend()
    plt.xticks(x+bar_width/2,tick_label)
    plt.xlabel("餐饮门店排名")
    plt.ylabel("顾客评论数")
    plt.show()
def function():#先按照评论人数排序排序,然后选择每个城市前五名的评论数做成并列条图
    dic1=df1.sort_values(by='评论数',ascending=False)
    dic2 = df2.sort_values(by='评论数', ascending=False)
    dic3 = df3.sort_values(by='评论数', ascending=False)
    se1=creatSeries(dic1)
    se2=creatSeries(dic2)
    se3=creatSeries(dic3)
    se1=se1[:5]
    se2=se2[:5]
    se3=se3[:5]
    print(se1)
    print(se2)
    print(se3)
    morebar(se1,se2,se3)
function()
  1. 最后,筛选出南京和苏州销量第一的餐饮店用户评价,分别保存为nanTop.csv和suzhouTop1.csv文件,使用python的jieba库进行分词,使用wordcloud库绘制南京:夫子庙美食街和苏州:哑巴生煎的用户评价词云图(词云图形状为该省的轮廓),如图4.1,4.2所示。

图4.1

出行数据可视化 旅游数据可视化_数据_11

图4.2

出行数据可视化 旅游数据可视化_html_12


制作简单词云图的代码如下(需要下载两个省的地图图片):

制作南京夫子庙美食街词云图代码:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import jieba
import wordcloud
from imageio import imread
plt.rcParams['font.sans-serif'] = ['SimHei'] # 在matplotlib中设置字体为SimHei黑体
plt.rcParams['axes.unicode_minus'] = False# 解决matplotlib坐标轴负号‘-’显示为方块问题
df1 = pd.read_excel(r"C:\Users\gcc\Desktop\nanjingfood.xlsx")
df2=pd.read_excel(r"C:\Users\gcc\Desktop\suzhou.xlsx")
df3=pd.read_excel(r"C:\Users\gcc\Desktop\wuxifood.xlsx")
def read(df1):
    lis=[]
    t5= np.array(df1["字段1"])[:1]
    t5=t5[0].split("票")[0]
    lis.append(eval(t5))
    t4 = np.array(df1["字段2"])[:1]
    t4 = t4[0].split("票")[0]
    lis.append(eval(t4))
    t3 = np.array(df1["字段3"])[:1]
    t3 = t3[0].split("票")[0]
    lis.append(eval(t3))
    t2 = np.array(df1["字段4"])[:1]
    t2 = t2[0].split("票")[0]
    lis.append(eval(t2))
    t1 = np.array(df1["字段5"])[:1]
    t1 = t1[0].split("票")[0]
    lis.append(eval(t1))
    return(lis)
def changetocsv():#制作南京top1夫子庙美食街词云图
    #df=df1['rev-txt']
    df=df2['rev-txt']
    df=pd.DataFrame(df)
    df.to_csv(r"C:/Users/gcc/Desktop/suzhouTop1.csv",index=False,encoding="utf-8")
def wordclo():
    jieba.add_word("生煎",freq=None,tag=None)
    with open("C:/Users/gcc/Desktop/suzhouTop1.csv", "r", encoding="utf-8") as f:
        txt = f.read()
    ex = [",", "(", ")", "\n", "的", "《", "》", " ", "\"", "了", "在", "是", "你", "我", "她", "他", "。", "?", "、", ".", "和",
          "等", "对", "都", "各", "“", "“", "”", "要", "就", "很", "还","有","也","吧","!","啊"]
    for i in ex:
        txt = txt.replace(i, "")
    words = jieba.lcut(txt)
    d = {}
    for i in words:
        d[i] = d.get(i, 0) + 1
    items = d.items()
    ite = list(items)
    ite.sort(key=lambda x: x[-1], reverse=True)
    ite = ite[:100]
    dic = dict(ite)
    print(dic)
    lis = list(dic.keys())
    txt = " ".join(lis)
    jpg = imread(r'C:/Users/gcc/Desktop/苏州.jpg')
    w = wordcloud.WordCloud(font_path="msyh.ttc", width=1000, height=800, max_words=100, background_color="white",mask=jpg)
    w.generate(txt)
    w.to_file("b.png")