Pyecharts项目实战:去长沙旅游有哪些好玩的?

来源:Python数据之道

作者:Peter

整理:Lemon

长沙,著名的"星城"、"娱乐之都",湖南省省会,素有“屈贾之乡”、“山水洲城”之名。每年都有很多的游客到长沙旅游,不仅是因为美食多,比如:文和友小龙虾、臭豆腐、糖油粑粑,以及网红奶茶茶颜悦色,还因为长沙真的有很多可以游逛的美景。

本文将结合实际的数据来告诉你,如果你想去长沙旅游,你应该去哪里玩。湖南欢迎你,长沙欢迎你!

系统环境

  • Macos

  • jupyter notebook

  • python3.7.5

  • Pyecharts 1.7.1

特别提醒下,Pyecharts 的版本一定要保持一致。

数据获取

导入库

首先我们需要导入各种库,这些库的主要功能如下

  • 爬虫请求发送及数据解析

  • csv 保存爬取到的数据

  • Pandas 处理爬取到的数据

  • Jieba 中文分词

  • Pyecharts 绘图专用库

import pandas as pd
import re
import csv
import json
import requests
import random

# 显示所有列
# pd.set_option('display.max_columns', None)

# 显示所有行
# pd.set_option('display.max_rows', None)

# 设置value的显示长度为100,默认为50
# pd.set_option('max_colwidth',100)

# 绘图相关
import jieba
import matplotlib.pyplot as plt
from pyecharts.globals import CurrentConfig, OnlineHostType   # 事先导入,防止不出图
from pyecharts import options as opts  # 配置项
from pyecharts.charts import Bar, Pie, Line, HeatMap, Funnel, WordCloud, Grid, Page  # 各个图形的类
from pyecharts.faker import Faker  # 自身数据
from pyecharts.commons.utils import JsCode   
from pyecharts.globals import ThemeType,SymbolType

爬取信息

先确定我们爬取的字段信息:

  1. 中文名:cn_title

  2. 英文名:en_title

  3. 排名:ranking

  4. 驴友百分比:lvyou

  5. 地点:location

  6. 简介:abstract

  7. 评论数:comment

  8. 攻略数:strategy

网页规律

首先看看网站 URL 的规律:

https://travel.qunar.com/p-cs300022-changsha-jingdian-1-1
https://travel.qunar.com/p-cs300022-changsha-jingdian-1-2
https://travel.qunar.com/p-cs300022-changsha-jingdian-1-3

我们发现是通过最后的数字来更新 URL 地址的,因此实际爬虫中我们构造了一个 URL 地址来循环爬取:

for i in range(1,165):
  "https://travel.qunar.com/p-cs300022-changsha-jingdian-1-{}".format(i)

单页源码获取

我们使用 requests 模块来发送爬虫请求:

url = "https://travel.qunar.com/p-cs300022-changsha-jingdian-1-1"

# 需要更换自己的请求头
headers = {"user-agent": "个人实际请求头"}

response = requests.get(url=url,headers=headers)
result = response.content.decode()

源码规律

每页有 10 个景点(最后一页可能没有),再看看每个页面源码的规律:每个 <li></li> 标签中代表一个景点

看看第一个景点(橘子洲)中 8 个字段信息所在的位置:

正则匹配单页数据

我们使用 re 模块通过正则表达式来解析需要爬取的字段。

关于正则表达式的使用,「Python数据之道」之前有过分享,大家可以点击链接前往:

如果解析的HTML源码中含有双引号,那么re.findall()方法后面的字符串的最外层使用单引号

1、景点中文名

# 1-景点中文名称

cn_title = re.findall('class="cn_tit">(.*?)<span class="en_tit">.*?</span>',result,re.S)
print("长度:",len(cn_title))
cn_title

2、景点英文名

# 2-景点英文名称

en_title = re.findall('<span class="en_tit">(.*?)</span>.*?</span>',result,re.S)
print("长度",len(en_title))
en_title

3、攻略数

4、点评数

# 4-comment点评数量

comment = re.findall('class="icon_comment" title="点评"></span>(.*?)</div>',result, re.S)
print(len(comment))
comment

5、地点

我们把 去过()的驴友 作为关键词。

# 5-景点地点:去过+地点+的驴友.....

location = re.findall('去过(.*?)的驴友', result, re.S)
print(len(location))
location

6、排名

排名的提取稍微复杂些,因为有些景点没有排名信息,需要特殊处理,我们看看两种不同的源码

存在排名的情形:

不存在排名的情形:

我们以前 10 页为例,先提取 ranking_sum 后面的全部内容,如果有排名则再进一步处理,没有排名则显示为 0 :

for i in range(1,5):
    url = "https://travel.qunar.com/p-cs300022-changsha-jingdian-1-{}".format(i)
    headers = {"user-agent": "个人请求头"}
    response = requests.get(url=url,headers=headers)
    result = response.content.decode()

    ranking = re.findall('class="ranking_sum">(.*?)</span>.*?</div>',result,re.S)
    print("原始数据{}:".format(i), ranking)
    
    # 这里才是提取ranking排名的关键代码
    for j in range(len(ranking)):
        if ranking[j] is "":
            ranking[j] = "0"   # 没有排名显示为0
        else:
            ranking[j] = ranking[j].split(">")[1]   # 根据>进行切割,取出右边的数据
    print("修改后数据{}:".format(i), ranking)
    print("-----------------------------")

7、驴友

# 有多少的驴友也去过这个地方

lvyou = re.findall('class="comment_sum">.*?class="sum">(.*?)</span>',result,re.S)
print(len(lvyou))
lvyou

8、景点简介

# 8-景点简介
abstract = re.findall('class="desbox">(.*?)</div>',result,re.S)
print(len(abstract))
abstract

全网数据

下面是全网爬取的代码,包含几个内容:

  • 请求网页获取源码

  • 解析源码得到每个字段信息

  • 保存到 csv 文件中

数据处理

读取数据

读取我们保存的数据:

我们关注的是长沙的相关景点,指定 location="长沙",同时查看数据中字段的类型:

ranking处理

我们对景区排名进行处理,取出前 20 个景区,同时需要将排名为 0 的数据排除掉:

# 如何修改某个字段的类型
# changsha["ranking"] = changsha["ranking"].astype(int)

# 根据ranking排序取出前20名数据,排除ranking=0的数据
changsha1 = changsha[changsha["ranking"] != 0].sort_values(by=["ranking"])[:20].reset_index(drop=True)
changsha1.head()

从上面的表格数据可以得到初步结论:

  • 网游眼中排名第一的是橘子洲;排名第二的是岳麓山。两个景点的确很出名;

  • 排名第三的黄兴路步行街和第四的太平老街都是在长沙雨花区的五一广场附近;

comment处理

评论 comment 处理,取出前 20 个评论最高的景点(降序):

changsha2 = changsha[changsha["comment"] != 0].sort_values(by=["comment"],ascending=False)[:20]
changsha2.head()

strategy 处理

对攻略数量是和上面评论处理相同,取出前 20 个(降序):

changsha3 = changsha[changsha["strategy"] != 0].sort_values(by=["strategy"],ascending=False)[:20]
changsha3.head()

lvyou处理

我们爬取到的驴友数据是百分比,类型是字符串类型,我们现在去掉 % 符号,取出左边的数值,如果没有则用 0 代替:

# 去掉%取出左边数据
changsha["lvyou_number"] = changsha["lvyou"].apply(lambda x:x.split("%")[0])

changsha["lvyou_number"] = changsha["lvyou_number"].astype(int)
changsha.head()

abstract 处理

简介是中文文字,我们接下来需要绘制词云图。我们先用jieba分词+去停用词的方法进行处理

1、简介全部转成一个大列表

abstract_list = changsha["abstract"].tolist()
abstract_list[:5]

2、使用 jieba 分词的效果

for i in range(len(abstract_list)):
    # str函数强制将数值转成字符串,才能使用strip方法
    seg_list = jieba.cut(str(abstract_list[i]).strip(), cut_all=False)  # seg_list只是一个generator生成器:<class 'generator'>
    print(("Default Mode: " + "/ ".join(seg_list)))  #  用list方法展开

3、分词结果放入一个列表中

# 3-将分词的结果全部放入一个列表中,方便后续处理

jieba_name = []

for i in range(len(abstract_list)):
    # seg_list只是一个generator生成器:<class 'generator'>
    seg_list = jieba.cut(str(abstract_list[i]).strip(), cut_all=False)  
    # 对list(seg_list)中的每个元素进行追加
    for each in list(seg_list):   
        jieba_name.append(each)

jieba_name[:20]

4、分词之后使用去停用词

停用词表是我自己在网上收集和整理的:

  • 先创建读取停用词表的数据的函数

  • 加载停用词之后再和现有的数据对比判断

# 创建停用词list
def stopwordslist(filepath):
    stopwords = [line.strip() for line in open(filepath, 'r', encoding='utf-8').readlines()]
    return stopwords

# 传入路径,加载去停用词
stopwords = stopwordslist('/Users/peter/Desktop/nlp_stopwords.txt')  

stopword_list = []
for word in jieba_name:  # jieba_name中的word不在停用词表中,且不是\t制表符
    if word not in stopwords:
        if word != "\t" and word != " " and word != "nan":  # 几个特殊符号的排除
            stopword_list.append(word)
            
stopword_list[:10]

5、统计单词个数

dic = {}
number = 0

for each in stopword_list:
    if each in dic.keys():
        number += 1   # 如果存在字典中则个数加1,并赋值给当前的each键
        dic[each] = number
    else:
        dic[each] = 1 # 不存在则结果为1,只出现一次

dic

结果的部分截图:

6、将上面的字典数据转成列表嵌套元组的形式,方便后续绘图:

tuple_list = []

for k,v in dic.items():
    tuple_list.append(tuple([k,v]))
tuple_list[:20]

绘图

关于Pyecharts桑葚图、饼图、组合图形的使用,「Python数据之道」之前有过分享,大家可以点击链接前往:

本文中绘制的图形如下:

  • ranking:折线图

  • comment:柱状图

  • strategy:漏斗图

  • lvyou:柱状图

  • abstract:词云图

pyecharts配置项说明:https://zhuanlan.zhihu.com/p/117467519

# 排名-折线图
def Ranking() -> Line:
    ranking = (
        Line()
        .add_xaxis(changsha1["cn_title"].tolist())
        .add_yaxis("景点排名",changsha1["ranking"].tolist())
        .set_global_opts(title_opts=opts.TitleOpts(title="长沙前20景点排名"),
                        xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-30)),  # 设置旋转角度
                        )
    )
    return ranking

# 评论-柱状图
def Comment() -> Bar:
    comment = (
        Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))
        .add_xaxis(changsha2["cn_title"].tolist())
        .add_yaxis("景点评论数",changsha2["comment"].tolist())
        .set_global_opts(title_opts=opts.TitleOpts(title="长沙前20最高的景点评论数"),
                         xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-36)),  
                        )
    )
    
    return comment

# 景点攻略-漏斗图
def Strategy() -> Funnel:
    strategy = (
        Funnel()
        .add("景点攻略", [list(z) for z in zip(changsha3["cn_title"].tolist(), changsha3["strategy"].tolist())])
        .set_global_opts(title_opts=opts.TitleOpts(title="景点攻略数漏斗",pos_left="center"),
                         legend_opts=opts.LegendOpts(type_="scroll", pos_left="1%", orient="vertical")   # 图例滚动,且靠左
                        )
    )
    
    return strategy


# 驴友数-柱状图
def LvYou() -> Bar:
    lvyou = (
        Bar(init_opts=opts.InitOpts(theme=ThemeType.WONDERLAND))
        .add_xaxis(changsha4["cn_title"].tolist())
        .add_yaxis("景点驴友数",changsha4["lvyou_number"].tolist())
        .set_global_opts(title_opts=opts.TitleOpts(title="长沙驴友最高的前10景点"),
                         xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-25)),  # 设置旋转角度
                        )
    )
    
    return lvyou

# 景点简介-词云图
def Abstract() -> WordCloud:
    abstract = (
        WordCloud()
        .add("", tuple_list, word_size_range=[12, 55])
        .set_global_opts(title_opts=opts.TitleOpts(title="长沙景点简介-词云图"))
    )
    
    return abstract

# Page类
def main():
    page = Page(layout=Page.DraggablePageLayout)
    # 将上面封装好的函数添加进来
    page.add(
        Ranking(),
        Comment(),
        Strategy(),
        LvYou(),
        Abstract()
    )
    page.render("changsha.html")
   
if __name__ == "__main__":
    main()

我们看看初步的效果:

看到效果中显示的图形,我们注意到两点:

  1. 左上角的 Save Config 按钮

  2. 整体图形靠左

  3. 图形是一个个上下排列

现在拖动图形和改变大小,排列成我们需要的形状和位置布局:

点击Save Config按钮,生成一个chart_config.json的文件,我们运行如下的代码,便会将我们的图形固定下来。

Page.save_resize_html("changsha.html",   # 上面的HTML文件名称
                      cfg_file="chart_config.json",  # 保存的json配置文件
                      dest="changsha_new.html")  # 新的文件名称

结论

1、橘子洲你真的应该去

从上面的好几种图形中看出来,不管是排名,还是游客的评论数,亦或者游客的攻略数,橘子洲都是排名第一,其附近的橘子洲烟火、橘子洲大桥等都是值得你去的第一个景点。

2、五一广场你也不能错过

天心区的黄兴路步行街、太平老街、火宫殿、天心阁、南门口是一整片的繁华区,店铺和年轻人特别多,爱吃爱喝爱玩的你,最应该去那里。

3、岳麓山散散步

从景区排名、网友的评论数、攻略数看出来,到长沙的朋友也特别爱去岳麓山。岳麓山、岳麓书院、爱晚亭是连在一起的,连湖南大学也在山脚下,也是一个很不错的景点。

4、逛逛博物馆和遗址

如果你喜欢历史,喜欢逛博物馆,那么你可不能错过:湖南省博物馆、马王堆汉墓遗址、长沙简牍博物馆,尤其是著名的马王堆汉墓遗址。

湖南长沙欢迎你

代码文件

为方便大家练习,可以在公号「柠檬数据」后台回复 “20210113”获取代码及数据文件。

柠檬数据

作者简介

Peter,硕士毕业僧一枚,从电子专业自学Python入门数据行业,擅长数据分析及可视化。喜欢数据,坚持跑步,热爱阅读,乐观生活。个人格言:不浮于世,不负于己

个人站点:www.renpeter.cn

拓展阅读

本文来自公众号读者投稿,欢迎各位童鞋向公号投稿,点击下面图片了解详情!


版权声明:本文为lemonbit原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。