作者:大吉大利小米醬
來源:http://www.lxweimin.com/p/b7627e67b6b9
前言:本文建議有一定Python基礎和前端(html,js)基礎的盆友閱讀,零基礎可以去看我之前的文。(咳咳,不能總更小白文,這樣顯得我不(mei)夠(you)專(xue)業(yè)(xi))。
金秋九月,丹桂飄香,在這秋高氣爽,陽光燦爛的收獲季節(jié)里,我們送走了一個個暑假余額耗盡哭著走向校園的孩籽們,又即將迎來一年一度偉大祖國母親的生日趴體(無心上班,迫不及待想為祖國母親慶生!)。
那么問題來了,去哪兒玩呢?百度輸了個"國慶",出來的第一條居然是"去哪里旅游人少"……emmmmmmm,因缺思廳。
于是我萌生了通過旅游網站的景點銷量來判斷近期各景點流量情況的想法(這個想法很危險啊)。
所以這次的目標呢,是爬去哪兒網景點頁面,并得到景點的信息,大家可以先思考下大概需要幾步。
1.百度的地圖API和echarts
因為前幾次爬蟲都是爬一些文本信息,做一下詞云之類的,中二の我覺得:沒!意!思!了!這次正好爬的是數據,我決定用數據的好基友——圖表來輸出我爬取的數據,也就是說我要用爬取的景點銷量以及景點的具體位置來生成一些可視化數據。
安利一下百度的地圖API和echarts,前者是專門提供地圖API的工具,聽說好多APP都在用它,后者是數據處理居家旅行的好伙伴,用了之后,它好,我也好(隱約覺得哪里不對)。
API是什么,API是應用程序的編程接口,就好像插頭與插座一樣,我們的程序需要電(這是什么程序?),插座中提供了電,我們只需要在程序中寫一個與插座匹配的插頭接口,就可以使用電來做我們想做的事情,而不需要知道電是如何產生的。
引入數據后的百度熱力圖
再按照我上一篇文的套路來講,米醬的小說寫完啦!但她還想把小說出成書,可是怎么出書捏?米醬不會呀,這時候米醬發(fā)現某出版社提供了出版服務,出版社表示只需要提供小說的正文、以及一個設計的封面就可以啦,于是米醬將小說保存成了word格式,又畫了個封面jpg圖,發(fā)給了出版社,沒過多久米醬就拿到了一本裝訂好的書啦(此段純屬虛構,專業(yè)出版人士盡管打我,我不會承認的)。
在米醬出書的過程中,米醬并不需要知道出版社是怎么印刷這個書的,也不需要知道是怎么裝訂這個書的,米醬只需要提供出版社所要求的東西即可。
通過api對♂接的開發(fā)者與服務商
2.確定輸出文件
有人可能說,我已經懂了api是啥意思了,可是咋個用呢。關于這一點,米醬很負責任的告訴你:我也不會。
但是!
百度地圖提供了很多API使用示例,有html基礎,大致可以看懂,有js基礎就可以嘗試改函數了(不會jsの我默默地復制源代碼),仔細觀察源代碼,可以知道熱力圖的生成主要的數據都存放在points這個變量中。
這種[{x:x,x:x},{x:x,x:x}]格式的數據,是一種json格式的數據,由于具有自我描述性,所以比較通俗易懂,大概可以知道這里的三個值,前倆個是經緯度,最后一個應該是權重(我猜的)。
也就是說,如果我希望將景點的熱門程度生成為熱力圖,我需要得到景點的經緯度,以及它的權重,景點的銷量可以作為權重,并且這個數據應該是json格式的呈現方式。
echarts也是一樣滴(^__^)。
3.爬取數據
其實這次的爬蟲部分是比較簡單的(如果你有跟著我的文爬過網站的話)。
分析網址(去哪兒景點)→爬取分頁中信息(景點經緯度、銷量)→轉為json文件。
分析去哪兒景點頁的網址可得出結構:搜索地點?ion=&from=mplsearchsuggest&page=頁數
這次沒有用正則來匹配內容,而使用了xpath匹配,肥腸好用。
defgetList():
place=raw_input('請輸入想搜索的區(qū)域、類型(如北京、熱門景點等):')
url='http://piao.qunar.com/ticket/list.htm?keyword='+str(place)+'?ion=&from=mpl_search_suggest&page={}'
i=1
sightlist=[]
whilei:
page=getPage(url.format(i))
selector=etree.HTML(page)
print'正在爬取第'+str(i)+'頁景點信息'
i+=1
informations=selector.xpath('//div[@class="result_list"]/div')
forinfininformations:#獲取必要信息
sight_name=inf.xpath('./div/div/h3/a/text()')[0]
sight_level=inf.xpath('.//span[@class="level"]/text()')
iflen(sight_level):
sight_level=sight_level[0].replace('景區(qū)','')
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()')
iflen(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)
returnsightlist,place
1.這里把每個景點的所有信息都爬下來了(其實是為了練習使用xpath……)。
2.使用了while循環(huán),for循環(huán)的break的方式是發(fā)現無銷量時給i值賦零,這樣while循環(huán)也會同時結束。
3.地址的匹配使用re.sub()函數去除了n多復雜信息,這點后面解釋。
4.輸出本地文本
為了防止代碼運行錯誤,為了維護代碼運行的和平,將輸出的信息列表存入到excel文件中了,方便日后查閱,很簡單的代碼,需要了解pandas的用法。
deflistToExcel(list,name):
df=pd.DataFrame(list,columns=['景點名稱','級別','所在區(qū)域','起步價','銷售量','熱度','地址','標語','詳情網址'])
df.to_excel(name+'景點信息.xlsx')
5.百度經緯度api
肥腸悲傷的,(?﹏?)我沒找到去哪兒景點的經緯度,以為這次學(zhuang)習(bi)計劃要就此流產了。(如果有人知道景點經緯度在哪里請告訴我)
但是,enhahhahahaha,我怎么會放棄呢,我又找到了百度經緯度api,網址:地址&output=json&ak=百度密鑰,修改網址里的"地址"和"百度密鑰",在瀏覽器打開,就可以看到經緯度的json信息。
#上海市東方明珠的經緯度信息
{"status":0,"result":{"location":{"lng":121.5064701060957,"lat":31.245341811634675},"precise":1,"confidence":70,"level":"UNKNOWN"}}
百度密鑰申請方法
這樣我就可以根據爬到的景點地址,查到對應的經緯度辣!python獲取經緯度json數據的代碼如下。
defgetBaiduGeo(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文件輸出到本地文件中。
defgetBaiduGeo(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
forlinlist:
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']
exceptKeyError,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']
exceptKeyError,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']
exceptKeyError,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)
withopen('./points.json',"w")asf:
f.write(bjsonlist)
withopen('./geoCoordMap.json',"w")asf:
f.write(ejsonlist1)
withopen('./data.json',"w")asf:
f.write(ejsonlist2)
(╯' - ')╯┻━┻
在設置獲取經緯度的地址時,為了匹配到更準確的經緯度,我選擇了匹配景點地址,然鵝,景點地址里有各種神奇的地址,帶括號解釋在XX對面的,說一堆你應該左拐右拐各種拐就能到的,還有英文的……于是就有了第三章中復雜的去除信息(我終于圓回來了!)。
然鵝,就算去掉了復雜信息,還有一些匹配不到的景點地址,于是我使用了嵌套try,如果景點地址匹配不到;就匹配景點名稱,如果景點名稱匹配不到;就匹配景點所在區(qū)域,如果依然匹配不到,那我……那我就……那我就跳過ㄒ_ㄒ……身為一個景點,你怎么能,這么難找呢!不要你了!
這里生成的三個json文件,一個是給百度地圖api引入用的,另倆個是給echarts引入用的。
6.網頁讀取json文件
將第二章中所述的百度地圖api示例中的源代碼復制到解釋器中,添加密鑰,保存為html文件,打開就可以看到和官網上一樣的顯示效果。echarts需要在實例頁面,點擊頁面右上角的EN切換到英文版,然后點擊download demo下載完整源代碼。
根據html導入json文件修改網頁源碼,導入json文件。
#百度地圖api示例代碼中各位置修改部分
http://www.google-analytics.com/ga.js"> src="">http://libs.baidu.com/jquery/2.0.0/jquery.js">
這里使用了jQuery之后,即使網頁調試成功了,在本地打開也無法顯示網頁了,在chrome中右鍵檢查,發(fā)現報錯提示是需要在服務器上顯示,可是,服務器是什么呢?
百度了一下,可以在本地創(chuàng)建一個服務器,在終端進入到html文件所在文件夾,輸入python -m SimpleHTTPServer,再在瀏覽器中打開http://127.0.0.1:8000/,記得要將html文件名設置成index.html哦~
7.后記
因為注冊但沒有認證開發(fā)者賬號,所以每天只能獲取6K個經緯度api(這是一個很好的偷懶理由),所以我選擇了熱門景點中前400頁(每頁15個)的景點,結果可想而知,(?﹏?)為了調試因為數據增多出現的額外bug,最終的獲取的景點數據大概在4k5條左右(爬取時間為2017年09月10日,爬取關鍵詞:熱門景點,僅代表當時銷量)。
熱門景點熱力圖
熱門景點示意圖
這些地圖上很火爆的區(qū)域,我想在國慶大概是這樣的
這樣的
還有這樣的
將地圖上熱門景點的銷量top20提取出來,大多數都是耳熟能詳的地點,帝都的故宮排在了第一位,而大四川則占據了top5中的三位,而排在top20中也四川省就占了6位,如果不是因為地震,我想還會有更多的火爆的景點進入排行榜的~這樣看來如果你這次國慶打算去四川的話,可以腦補到的場景就是:人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人……
熱門景點銷量top20
于是我又做了一個各城市包含熱門景點數目的排行,沒想到在4千多個熱門景點中,數目最多的竟是我大浙江,是第二個城市的1.5倍,而北京作為首都也……可以說是景點數/總面積的第一位了。
主要城市熱門景點數.png
這些城市有辣么多熱門景點,都是些什么級別的景點呢?由下圖看來,各城市的各級別景點基本與城市總熱門景點呈正相關,而且主要由4A景區(qū)貢獻而來。
主要城市熱門景點級別
既然去哪些地方人多,去哪里景多都已經知道了,那再看看去哪些地方燒得錢最多吧?下圖是由各城市景點銷售起步價的最大值-最小值扇形組成的圓,其中湖北以單景點銷售起步價600占據首位,但也可以看到,湖北的景點銷售均價并不高(在紅色扇形中的藏藍色線條)。而如果國慶去香港玩,請做好錢包減肥的心理和生理準備(??ω??)?。
各省旅游景點銷售起步價
好啦分析完啦,ヾ(*ΦωΦ)ツ大家可要好好玩呀。