金秋玄月,丹桂飘喷鼻香,在这天高气爽,阳光残酷的收成时令里,我们送走了一个个暑假余额耗尽哭着走向校园的孩子们,又即将迎来一年一度伟大祖国母亲的生日趴体(无心上班,迫不及待想为祖国母亲庆生)。
那么问题来了,去哪儿玩呢?百度输了个“国庆”,出来的第一条居然是“去哪里旅游人少”……emmmmmmm,因缺思厅。
于是我萌生了通过旅游网站的景点销量来判断近期各景点流量情形的想法(这个想法很危险啊)。
以是这次的目标呢,是爬去哪儿网景点页面,并得到景点的信息,大家可以先思考下大概须要几步。
本文建议有一定 Python 根本和前端(html,js)根本的朋友阅读,零根本可以去看我之前的文。(咳咳,不能总更小白文,这样显得我不(mei)够(you)专(xue)业(xi))。
百度的舆图 API 和 echarts
由于前几次爬虫都是爬一些文本信息,做一下词云之类的,我以为:没!意!思!了!这次恰好爬的是数据,我决定用数据的好基友——图表来输出我爬取的数据,也便是说我要用爬取的景点销量以及景点的详细位置来天生一些可视化数据。
安利一下百度的舆图 API 和 echarts,前者是专门供应舆图 API 的工具,听说好多 APP 都在用它,后者是数据处理居家旅行的好伙伴,用了之后,它好,我也好(隐约以为哪里不对)。
API 是什么,API 是运用程序的编程接口,就彷佛插头与插座一样,我们的程序须要电(这是什么程序?),插座中供应了电,我们只须要在程序中写一个与插座匹配的插头接口,就可以利用电来做我们想做的事情,而不须要知道电是如何产生的。
引入数据后的百度热力争
通过 API 对接的开拓者与做事商
确定输出文件
有人可能说,我已经懂了 API 是啥意思了,可是咋个用呢。关于这一点,我很负任务的见告你:我也不会。
但是!百度舆图供应了很多 API 利用示例,有 html 根本,大致可以看懂,有 js 根本就可以考试测验改函数了(不会 js 的,我默默地复制源代码),仔细不雅观察源代码,可以知道热力争天生的紧张数据都存放在 points 这个变量中。
这种[{x:x,x:x},{x:x,x:x}]格式的数据,是一种 json 格式的数据,由于具有自我描述性,以是比较普通易懂,大概可以知道这里的三个值,前两个是经纬度,末了一个该当是权重(我猜的)。
也便是说,如果我希望将景点的热门程度天生为热力争,我须要得到景点的经纬度,以及它的权重,景点的销量可以作为权重,并且这个数据该当是 json 格式的呈现办法。
echarts 也是一样滴(^__^)。
爬取数据
这次的爬虫部分是比较大略的。剖析网址(去哪儿景点)→爬取分页中信息(景点经纬度、销量)→转为 json 文件。
剖析去哪儿景点页的网址,可得出构造:http://piao.qunar.com/ticket/list.htm?keyword=搜索地点®ion=&from=mpl_search_suggest&page=页数
这次没有用正则来匹配内容,而利用了 xpath 匹配,非常好用。
def getList(): place = raw_input('请输入想搜索的区域、类型(如北京、热门景点等):') url = 'http://piao.qunar.com/ticket/list.htm?keyword='+ str(place) +'®ion=&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 哦!
后记
由于注册但没有认证开拓者账号,以是每天只能获取 6K 个经纬度 API(这是一个很好的偷
结果可想而知,(ಥ﹏ಥ)为了调试由于数据增多涌现的额外 Bug,终极的获取的景点数据大概在 4500 条旁边(爬取韶光为 2017 年 9 月 10 日,爬取关键词:热门景点,仅代表当时销量)。
热门景点热力争
热门景点示意图
这些舆图上很火爆的区域,我想在国庆大概是这样的
这样的
还有这样的
将舆图上热门景点的销量 Top20 提取出来,大多数都是耳熟能详的地点,帝都的故宫排在了第一位,而大四川则霸占了 Top5 中的三位,排在 Top20 中四川省景点就占了 6 位。
如果不是由于地震,我想还会有更多的火爆的景点进入排行榜的~这样看来如果你这次国庆打算去四川的话,可以脑补到的场景便是:大家大家大家大家大家大家大家大家大家大家大家大家大家大家大家大家大家大家大家大家大家大家大家大家大家……
热门景点销量Top20
于是我又做了一个各城市包含热门景点数目的排行,没想到在 4 千多个热门景点中,数目最多的竟是我大浙江,是第二个城市的 1.5 倍,而北京作为都城也……可以说是景点数/总面积的第一位了。
紧张城市热门景点数
这些城市有辣么多热门景点,都是些什么级别的景点呢?由下图看来,各城市的各级别景点基本与城市总热门景点呈正干系,而且紧张由 4A 景区贡献而来。
紧张城市热门景点级别
既然去哪些地方人多,去哪里景多都已经知道了,那再看看去哪些地方烧得钱最多吧。
下图是由各城市景点发卖起步价的最大值-最小值扇形组成的圆,个中湖北以单景点发卖起步价 600 霸占首位。
但也可以看到,湖北的景点发卖均价并不高(在赤色扇形中的藏蓝色线条)。而如果国庆去喷鼻香港玩,请做好钱包减肥的生理和生理准备(•̀ω•́)✧。
各省旅游景点发卖起步价
好啦剖析完啦,ヾ(ΦωΦ)ツ大家可要好好玩呀。
PS:写了个网页,展示百度舆图的热力争效果和 echarts 的景点排行榜,方便大家查看。
热力度效果:http://easyinfo.online
gayhub源码:https://github.com/otakurice/notravellist/tree/master
写完这篇文的时候创造 echarts 有针对 Python 的模块可以引入,以是打算去学一下 Django、Flask 之类的 Web 框架,最近会更一些纯理论的意识流文,大家一起进步吧~
参考资料:
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
末了,怀着敬畏又惋惜的心情纪念一下 WePhone 创始人苏享茂,在发生自尽事宜之前我不认识他,我也不肯望以这种办法认识他,希望程序员的天下永久纯挚、没有敲诈。