用python爬蟲爬取去哪兒4500個熱門景點,看看國慶不能去哪兒

前言:本文建議有必定Python基礎和前端(html,js)基礎的盆友閱讀。javascript

金秋九月,丹桂飄香,在這秋高氣爽,陽光燦爛的收穫季節裏,咱們送走了一個個暑假餘額耗盡哭着走向校園的孩籽們,又即將迎來一年一度偉大祖國母親的生日趴體(無意上班,火燒眉毛想爲祖國母親慶生!)。php

那麼問題來了,去哪兒玩呢?百度輸了個「國慶」,出來的第一條竟然是「去哪裏旅遊人少」……emmmmmmm,因缺思廳。html

因而我萌生了經過旅遊網站的景點銷量來判斷近期各景點流量狀況的想法(這個想法很危險啊)。前端

因此此次的目標呢,是爬去哪兒網景點頁面,並獲得景點的信息,你們能夠先思考下大概須要幾步。java

1.百度的地圖API和echarts

此次正好爬的是數據,我決定用數據的好基友——圖表來輸出我爬取的數據,也就是說我要用爬取的景點銷量以及景點的具體位置來生成一些可視化數據。python

安利一下百度的地圖APIecharts,前者是專門提供地圖API的工具,據說好多APP都在用它,後者是數據處理居家旅行的好夥伴,用了以後,它好,我也好(隱約以爲哪裏不對)。jquery

API是什麼,API是應用程序的編程接口,就好像插頭與插座同樣,咱們的程序須要電(這是什麼程序?),插座中提供了電,咱們只須要在程序中寫一個與插座匹配的插頭接口,就可使用電來作咱們想作的事情,而不須要知道電是如何產生的。git

引入數據後的百度熱力圖

再詳細一點講呢,就比如米醬的小說寫完啦!但她還想把小說出成書,但是怎麼出書捏?米醬不會呀,這時候米醬發現某出版社提供了出版服務,出版社表示只須要提供小說的正文、以及一個設計的封面就能夠啦,因而米醬將小說保存成了word格式,又畫了個封面jpg圖,發給了出版社,沒過多久米醬就拿到了一本裝訂好的書啦(此段純屬虛構,專業出版人士儘管打我,我不會認可的)。github

在米醬出書的過程當中,米醬並不須要知道出版社是怎麼印刷這個書的,也不須要知道是怎麼裝訂這個書的,米醬只須要提供出版社所要求的東西便可。web

經過api對♂接的開發者與服務商

2.肯定輸出文件

有人可能說,我已經懂了api是啥意思了,但是咋個用呢。關於這一點,米醬很負責任的告訴你:我也不會

可是!

百度地圖提供了不少API使用示例,有html基礎,大體能夠看懂,有js基礎就能夠嘗試改函數了(不會jsの我默默地複製源代碼),仔細觀察源代碼,能夠知道熱力圖的生成主要的數據都存放在points這個變量中。

這種[{x:x,x:x},{x:x,x:x}]格式的數據,是一種json格式的數據,因爲具備自我描述性,因此比較通俗易懂,大概能夠知道這裏的三個值,前倆個是經緯度,最後一個應該是權重(我猜的)。

也就是說,若是我但願將景點的熱門程度生成爲熱力圖,我須要獲得景點的經緯度,以及它的權重,景點的銷量能夠做爲權重,而且這個數據應該是json格式的呈現方式。

echarts也是同樣滴(*^__^*)。

3.爬取數據

其實此次的爬蟲部分是比較簡單的(若是你有跟着個人文爬過網站的話)。

分析網址(去哪兒景點)→爬取分頁中信息(景點經緯度、銷量)→轉爲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

1.這裏把每一個景點的全部信息都爬下來了(實際上是爲了練習使用xpath……)。
2.使用了while循環,for循環的break的方式是發現無銷量時給i值賦零,這樣while循環也會同時結束。
3.地址的匹配使用re.sub()函數去除了n多複雜信息,這點後面解釋。

4.輸出本地文本

爲了防止代碼運行錯誤,爲了維護代碼運行的和平,將輸出的信息列表存入到excel文件中了,方便往後查閱,很簡單的代碼,須要瞭解pandas的用法。

def listToExcel(list,name):
    df = pd.DataFrame(list,columns=['景點名稱','級別','所在區域','起步價','銷售量','熱度','地址','標語','詳情網址'])
    df.to_excel(name + '景點信息.xlsx')

5.百度經緯度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"}}

百度密鑰申請方法

這樣我就能夠根據爬到的景點地址,查到對應的經緯度辣!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引入用的。

6.網頁讀取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哦~

7.後記

由於註冊但沒有認證開發者帳號,因此天天只能獲取6K個經緯度api(這是一個很好的偷懶理由),因此我選擇了熱門景點中前400頁(每頁15個)的景點,結果可想而知,(ಥ﹏ಥ)爲了調試由於數據增多出現的額外bug,最終的獲取的景點數據大概在4k5條左右(爬取時間爲2017年09月10日,爬取關鍵詞:熱門景點,僅表明當時銷量)。

熱門景點熱力圖

熱門景點示意圖

這些地圖上很火爆的區域,我想在國慶大概是這樣的

這樣的

還有這樣的

將地圖上熱門景點的銷量top20提取出來,大多數都是耳熟能詳的地點,帝都的故宮排在了第一位,而大四川則佔據了top5中的三位,而排在top20中也四川省就佔了6位,若是不是由於地震,我想還會有更多的火爆的景點進入排行榜的~這樣看來若是你此次國慶打算去四川的話,能夠腦補到的場景就是:人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人……

熱門景點銷量top20

因而我又作了一個各城市包含熱門景點數目的排行,沒想到在4千多個熱門景點中,數目最多的竟是我大浙江,是第二個城市的1.5倍,而北京做爲首都也……能夠說是景點數/總面積的第一位了。

主要城市熱門景點數

這些城市有辣麼多熱門景點,都是些什麼級別的景點呢?由下圖看來,各城市的各級別景點基本與城市總熱門景點呈正相關,並且主要由4A景區貢獻而來。

主要城市熱門景點級別

既然去哪些地方人多,去哪裏景多都已經知道了,那再看看去哪些地方燒得錢最多吧?下圖是由各城市景點銷售起步價的最大值-最小值扇形組成的圓,其中湖北以單景點銷售起步價600佔據首位,但也能夠看到,湖北的景點銷售均價並不高(在紅色扇形中的藏藍色線條)。而若是國慶去香港玩,請作好錢包減肥的心理和生理準備(•̀ω•́)✧。

各省旅遊景點銷售起步價

好啦分析完啦,ヾ(*ΦωΦ)ツ你們可要好好玩呀。

PS:寫了個網頁,展現百度地圖的熱力圖效果和echarts的景點排行榜,方便你們查看。http://easyinfo.online 源碼已經上傳到gayhub啦~寫完這篇文的時候發現echarts有針對python的模塊能夠引入,因此打算去學一下Django、Flask之類的web框架,最近會更一些純理論的意識流文,你們一塊兒進步吧~

建了一個qq羣,歡迎各位來交♂流學♀習→python交友娛樂會所:613176398

相關文章
相關標籤/搜索