4500个热门景点数据,告诉你国庆长假的正确打开姿势

国庆出游,确实是个让人头痛的问题。今天这位数据侠,不仅用数据告诉你国庆如何成功避开“people mountain people sea”,还手把手带你用Python上手全套操作。

作者 | 大吉大利小米酱(本文首发于简书,已获授权)

金秋九月,丹桂飘香,在这秋高气爽,阳光灿烂的收获季节里,我们送走了一个个暑假余额耗尽哭着走向校园的孩子们,又即将迎来一年一度伟大祖国的生日趴体(无心上班,迫不及待想为祖国庆生)。

那么问题来了,去哪儿玩呢?百度输了个“国庆”,出来的第一条居然是“去哪里旅游人少”……emmmmmmm,因缺思厅。

于是我萌生了通过旅游网站的景点销量来判断近期各景点流量情况的想法(这个想法很危险啊)。

所以这次的目标呢,是爬去哪儿网景点页面,并得到景点的信息,大家可以先思考下大概需要几步。

使用工具:百度的地图API和echarts

安利一下百度地图API和echarts,前者是专门提供地图API的工具,听说好多APP都在用它,后者是数据处理居家旅行的好伙伴。

API是什么,API是应用程序的编程接口,就好像插头与插座一样,我们的程序需要电(这是什么程序?),插座中提供了电,我们只需要在程序中写一个与插座匹配的插头接口,就可以使用电来做我们想做的事情,而不需要知道电是如何产生的。

(图片说明:引入数据后的百度热力图)

(图片说明:通过API对接的开发者与服务商)

第一步:确定输出文件:

百度地图提供了很多API使用示例,有html基础,大致可以看懂,有js基础就可以尝试改函数了,仔细观察源代码,可以知道热力图生成的主要数据都存放在points这个变量中。

这种[{x:x,x:x},{x:x,x:x}]格式的数据,是一种json格式的数据,由于具有自我描述性,所以比较通俗易懂,大概可以知道这里的三个值,前两个是经纬度,最后一个应该是权重(我猜的)。

也就是说,如果我希望将景点的热门程度生成为热力图,我需要得到景点的经纬度,以及它的权重,景点的销量可以作为权重,并且这个数据应该是 json格式的呈现方式。

echarts也是一样的。

第二步:爬取数据

这次的爬虫部分是比较简单的。分析网址(去哪儿景点)→爬取分页中信息(景点经纬度、销量)→转为json文件。

分析去哪儿景点页的网址,可得出结构:

http://piao.qunar.com/ticket/list.htm?keyword=搜索地点&region=&from=mpl_search_suggest&page=页数

这次没有用正则来匹配内容,而使用了xpath匹配,非常好用。

def getList():
    place = raw_input('请输入想搜索的区域、类型(如北京、热门景点等):')
    url = 'http://piao.qunar.com/ticket/list.htm?keyword='+ str(place) +'&region=&from=mpl_search_suggest&page={}'
    i = 1
    sightlist = []
    while i:
        page = getPage(url.format(i))
        selector = etree.HTML(page)
        print '正在爬取第' + str(i) + '页景点信息'
        i+=1
        informations = selector.xpath('//div[@class="result_list"]/div')
        for inf in informations: #获取必要信息
            sight_name = inf.xpath('./div/div/h3/a/text()')[0]
            sight_level = inf.xpath('.//span[@class="level"]/text()')
            if len(sight_level):
                sight_level = sight_level[0].replace('景区','')
            else:
                sight_level = 0
            sight_area = inf.xpath('.//span[@class="area"]/a/text()')[0]
            sight_hot = inf.xpath('.//span[@class="product_star_level"]//span/text()')[0].replace('热度 ','')
            sight_add = inf.xpath('.//p[@class="address color999"]/span/text()')[0]
            sight_add = re.sub('地址:|(.*?)|\(.*?\)|,.*?$|\/.*?$','',str(sight_add))
            sight_slogen = inf.xpath('.//div[@class="intro color999"]/text()')[0]
            sight_price = inf.xpath('.//span[@class="sight_item_price"]/em/text()')
            if len(sight_price):
                sight_price = sight_price[0]
            else:
                i = 0
                break
            sight_soldnum = inf.xpath('.//span[@class="hot_num"]/text()')[0]
            sight_url = inf.xpath('.//h3/a[@class="name"]/@href')[0]
            sightlist.append([sight_name,sight_level,sight_area,float(sight_price),int(sight_soldnum),float(sight_hot),sight_add.replace('地址:',''),sight_slogen,sight_url])
        time.sleep(3)
    return sightlist,place
  • 这里把每个景点的所有信息都爬下来了(其实是为了练习使用 xpath)。
  • 使用了while循环,for循环的break的方式是发现无销量时给 i 值赋零,这样while循环也会同时结束。
  • 地址的匹配使用 re.sub() 函数去除了n多复杂信息,这点后面解释。

第四步:输出本地文本

为了防止代码运行错误,维护代码运行的和平,将输出的信息列表存入到excel文件中了,方便日后查阅,很简单的代码,需要了解pandas的用法。

def listToExcel(list,name):
    df = pd.DataFrame(list,columns=['景点名称','级别','所在区域','起步价','销售量','热度','地址','标语','详情网址'])
    df.to_excel(name + '景点信息.xlsx')

第五步:百度经纬度API

非常悲伤的,(ಥ﹏ಥ)我没找到去哪儿景点的经纬度,以为这次学(zhuang)习(bi)计划要就此流产了(如果有人知道景点经纬度在哪里请告诉我)。

但是,enhahhahahaha,我怎么会放弃呢,我又找到了百度经纬度 API。

网址:

http://api.map.baidu.com/geocoder/v2/?address=地址&output=json&ak=百度密钥

修改网址里的“地址”和“百度密钥”,在浏览器打开,就可以看到经纬度的json信息。

#上海市东方明珠的经纬度信息
{"status":0,"result":{"location":{"lng":121.5064701060957,"lat":31.245341811634675},"precise":1,"confidence":70,"level":"UNKNOWN"}}

百度密钥申请方法:http://jingyan.baidu.com/article/363872eccda8286e4aa16f4e.html

这样我就可以根据爬到的景点地址,查到对应的经纬度辣!Python获取经纬度json数据的代码如下:

def getBaiduGeo(sightlist,name):
    ak = '密钥'
    headers = {
    'User-Agent' :'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36'
    }
    address = 地址
    url = 'http://api.map.baidu.com/geocoder/v2/?address=' + address  + '&output=json&ak=' + ak
    json_data = requests.get(url = url).json()
    json_geo = json_data['result']['location']

观察获取的json文件,location中的数据和百度API所需要的json格式基本是一样,还需要将景点销量加入到json文件中,这里可以了解一下json的浅拷贝和深拷贝知识,最后将整理好的json文件输出到本地文件中。

def getBaiduGeo(sightlist,name):
    ak = '密钥'
    headers = {
    'User-Agent' :'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36'
    }
    list = sightlist
    bjsonlist = []
    ejsonlist1 = []
    ejsonlist2 = []
    num = 1
    for l in list:
        try:
            try:
                try:
                    address = l[6]
                    url = 'http://api.map.baidu.com/geocoder/v2/?address=' + address  + '&output=json&ak=' + ak
                    json_data = requests.get(url = url).json()
                    json_geo = json_data['result']['location']
                except KeyError,e:
                    address = l[0]
                    url = 'http://api.map.baidu.com/geocoder/v2/?address=' + address  + '&output=json&ak=' + ak
                    json_data = requests.get(url = url).json()
                    json_geo = json_data['result']['location']
            except KeyError,e:
                    address = l[2]
                    url = 'http://api.map.baidu.com/geocoder/v2/?address=' + address  + '&output=json&ak=' + ak
                    json_data = requests.get(url = url).json()
                    json_geo = json_data['result']['location']
        except KeyError,e:
            continue
        json_geo['count'] = l[4]/100
        bjsonlist.append(json_geo)
        ejson1 = {l[0] : [json_geo['lng'],json_geo['lat']]}
        ejsonlist1 = dict(ejsonlist1,**ejson1)
        ejson2 = {'name' : l[0],'value' : l[4]/100}
        ejsonlist2.append(ejson2)
        print '正在生成第' + str(num) + '个景点的经纬度'
        num +=1
    bjsonlist =json.dumps(bjsonlist)
    ejsonlist1 = json.dumps(ejsonlist1,ensure_ascii=False)
    ejsonlist2 = json.dumps(ejsonlist2,ensure_ascii=False)
    with open('./points.json',"w") as f:
        f.write(bjsonlist)
    with open('./geoCoordMap.json',"w") as f:
        f.write(ejsonlist1)
    with open('./data.json',"w") as f:
        f.write(ejsonlist2)

在设置获取经纬度的地址时,为了匹配到更准确的经纬度,我选择了匹配景点地址,然而,景点地址里有各种神奇的地址,带括号解释在XX对面的,说一堆你应该左拐右拐各种拐就能到的,还有英文的……

然而,就算去掉了复杂信息,还有一些匹配不到的景点地址,于是我使用了嵌套try,如果景点地址匹配不到;就匹配景点名称,如果景点名称匹配不到;就匹配景点所在区域,如果依然匹配不到,那我……那我就……那我就跳过ㄒ_ㄒ……

这里生成的三个json文件,一个是给百度地图API引入用的,另两个是给 echarts引入用的。

第六步:网页读取json文件

将第二章中所述的百度地图API示例中的源代码复制到解释器中,添加密钥,保存为html文件,打开就可以看到和官网上一样的显示效果。

echarts需要在实例页面,点击页面右上角的EN切换到英文版,然后点击 download demo下载完整源代码。

根据html导入json文件修改网页源码,导入json文件。

#百度地图api示例代码中各位置修改部分
<head>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.js"></script>
</head>
<script type="text/javascript">
    $.getJSON("points.json", function(data){
        var points = data;
        script中原有函数;
        });
</script>

这里使用了jQuery之后,即使网页调试成功了,在本地打开也无法显示网页了,在chrome中右键检查,发现报错提示是需要在服务器上显示,可是,服务器是什么呢?

百度了一下,可以在本地创建一个服务器,在终端进入到 html 文件所在文件夹,输入python -m SimpleHTTPServer,再在浏览器中打开 http://127.0.0.1:8000/,记得要将 html 文件名设置成 index.html 哦!

结果

最终的获取的景点数据大概在4500条左右,爬取关键词:热门景点,仅代表当时销量。

(图片说明:热门景点热力图)

(图片说明:热门景点示意图)

将地图上热门景点的销量Top20提取出来,大多数都是耳熟能详的地点,帝都的故宫排在了第一位,而大四川则占据了Top5中的三位,排在Top20中四川省景点就占了 6 位。

如果不是因为地震,我想还会有更多的火爆的景点进入排行榜的~这样看来如果你这次国庆打算去四川的话,可以脑补到的场景就是:人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人……

(图片说明:热门景点销量Top20)

于是我又做了一个各城市包含热门景点数目的排行,没想到在4千多个热门景点中,数目最多的竟是浙江,是第二个城市的1.5倍,而北京作为首都也……可以说是景点数/总面积的第一位了。

(图片说明:主要城市热门景点数)

这些城市有辣么多热门景点,都是些什么级别的景点呢?由下图看来,各城市的各级别景点基本与城市总热门景点呈正相关,而且主要由4A景区贡献而来。

(图片说明:主要城市热门景点级别)

既然去哪些地方人多,去哪里景多都已经知道了,那再看看去哪些地方烧得钱最多吧。

下图是由各城市景点销售起步价的最大值-最小值扇形组成的圆,其中湖北以单景点销售起步价600占据首位。但也可以看到,湖北的景点销售均价并不高(在红色扇形中的藏蓝色线条)。而如果国庆去香港玩,请做好钱包减肥的心理和生理准备。

(图片说明:各省旅游景点销售起步价)

好了,完美分析,提前祝大家国庆快乐!

写了个网页,展示百度地图的热力图效果和 echarts 的景点排行榜,方便大家查看。

热力度效果:http://easyinfo.online

gayhub源码:https://github.com/otakurice/notravellist/tree/master

参考资料:

1.地图API:http://developer.baidu.com/map/reference/index.php

2.echarts:http://echarts.baidu.com/

3.API使用示例:http://developer.baidu.com/map/jsdemo.htm#c1_15

4.json:http://www.runoob.com/json/json-tutorial.html

5.xpath:http://www.runoob.com/xpath/xpath-tutorial.html

6.pandas:http://python.jobbole.com/84416/

7.百度经纬度api:http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding

8.浅拷贝和深拷贝:http://python.jobbole.com/82294/

9.html导入json文件:http://www.jb51.net/article/36678.htm

数据侠门派

本文数据侠为大吉大利小米酱,电子科技大学毕业的机械设计喵一枚,致力于带领Python小白们打破《从入门到放弃》的魔咒,梦想成为一名程序媛,坐标杭州~

原文发布于微信公众号 - DT数据侠(DTdatahero)

原文发表时间:2017-09-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

你的CAD图纸被盗了吗?

本次分析的样本是CAD脚本动态生产的一种VBS蠕虫病毒,大概10多年前就已经开始通过E-Mail传播此样本,而如今这类病毒依旧活跃着,浮浮沉沉,生生不息。 蠕虫...

21780
来自专栏玄魂工作室

Equation Group(方程式组织)

众所周知,Equation Group这一黑客团伙与美国国家安全局(NSA)的关系一直十分密切。而且外界也普遍认为,Equation Group是美国国家安全局...

390100
来自专栏CDA数据分析师

我用 Python 爬取了全国 4500 个热门景点,告诉你国庆哪里去不得?

金秋九月,丹桂飘香,在这秋高气爽,阳光灿烂的收获季节里,我们送走了一个个暑假余额耗尽哭着走向校园的孩子们,又即将迎来一年一度伟大祖国母亲的生日趴体(无心上班,迫...

39270
来自专栏SAP最佳业务实践

SAP S/4 HANA新变化-信用管理后台配置

一、信用评估 1、定义信用段 ? 客户信用主数据就维护在信用段视图,信用段会和信用控制范围进行关联。 2、定义信用分级 ? 3、定义信用得分计算公式 ? 4...

56050
来自专栏程序员互动联盟

【答疑释惑】Makefile是什么,Windows下面如何编写?

1 问题 ? 解答: ? 点评:Windows下用微软的VS是不需要自己编写Makefile的,但是如果使用Cygwin之类的编译环境,还是需要自己编写Mak...

37390
来自专栏黑白安全

WiFi联盟宣布WPA3协议已最终完成 安全性增加

WiFi联盟(Wi-Fi Alliance)周一宣布WPA3协议已最终完成,这是WiFi连接的新标准。

6620
来自专栏域名资讯

英文域名chosen.com超22万元易主

英文域名,在国外属于非常吃香的一种域名,但一些基本常用被熟知的英文词汇的英文域名,在中国市场上却是依旧吃香,价格也保持着良好的势头。

19550
来自专栏漏斗社区

专属| 这是一个能降级HTTPS的恶意软件

今天是高考的最后一天啦!许多可爱的小萌新们将踏上一段新的人生旅程。多年的友情与青春终究在一次次相聚与别离中散场,各奔东西,未来的时光愿各自安好!愿放榜的日子里,...

15250
来自专栏FreeBuf

“脏牛漏洞”恶意Root软件分析报告

一、分析背景 近日,国际知名安全厂商趋势科技发布了名为《ZNIU: First Android Malware to Exploit Dirty COW Vul...

319100
来自专栏FreeBuf

只用一台笔记本发动DDoS攻击,就能让大型服务器下线

最近有研究人员发现了一种被称为BlackNurse的简单攻击方式,能够让独立入侵者能用有限的资源(一个有15Mbps带宽的笔记本)驱动大规模DDoS攻击,直接将...

280100

扫码关注云+社区

领取腾讯云代金券