1. 程式人生 > >手把手教你用Python抓取熱門景點熱力圖!(附程式碼)

手把手教你用Python抓取熱門景點熱力圖!(附程式碼)

640?wx_fmt=png&wxfrom=5&wx_lazy=1

來源:CSDN

本文長度為2500字,建議閱讀6分鐘

本文教你使用Python抓取熱力圖,告訴你國慶哪裡人最多?

國慶倒計時1天,我們即將迎來8天的小長假,相信很多小夥伴們已經提前規劃國慶去哪兒?你是選擇去人最少的單位加班呢?還是選擇人山人海的景點觀光?

如果去人最少的單位加班,一定要記得告知老闆,以望升職加薪走上人生巔峰;

如果選擇人山人海的景點,一定要提前檢視攻略,比如下文中使用 Python 技術抓取熱門景點的熱力圖。

金秋九月,丹桂飄香,在這秋高氣爽,陽光燦爛的收穫季節裡,我們送走了一個個暑假餘額耗盡哭著走向校園的孩子們,又即將迎來一年一度偉大祖國母親的生日趴體(無心上班,迫不及待想為祖國母親慶生)。

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1

那麼問題來了,去哪兒玩呢?

百度輸了個“國慶”,出來的第一條居然是“國慶去哪裡旅遊人少”。

640?wx_fmt=png

於是我想到可否通過旅遊網站的景點銷量來判斷近期各景點流量情況呢?本文按去哪兒網景點頁面,得到景點的資訊。

1.百度的地圖API和echarts

本次主要抓取的內容是資料,因此我決定用圖表來輸出抓取的資料,也就意味著我要用爬取的景點銷量以及景點的具體位置來生成一些視覺化資料。

在這裡使用的是百度的地圖API和echarts,前者是專門提供地圖API的工具,後者是資料處理居家旅行的好夥伴。

那麼API是什麼?API是應用程式的程式設計介面,就好像插頭與插座一樣,我們的程式需要電,插座中提供了電,我們只需要在程式中寫一個與插座匹配的插頭介面,就可以使用電來做我們想做的事情,而不需要知道電是如何產生的。

640?wx_fmt=png

引入資料後的百度熱力圖

而具體流程如下,好比出書的過程,想要將完結的小說出書,首先要通過出版社的出版服務,提供正文內容和封面設計圖,將儲存好的 word 文字和封面圖發給出版社,由此得到一本書。在此過程中,並不需要了解出版社印刷、裝訂的過程。

640?wx_fmt=png

通過API對接的開發者與服務商

2.確定輸出檔案

百度地圖提供了很多API使用示例,有HTML基礎,有JS基礎就可以嘗試改函數了。仔細觀察原始碼,可以知道熱力圖的生成主要的資料都存放在points這個變數中。

640?wx_fmt=png

這種[{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) 

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

  • 這裡把每個景點的所有資訊都抓取下來。

  • 使用了while迴圈,for迴圈的break的方式是發現無銷量時給i值賦零,這樣while迴圈也會同時結束。

  • 地址的匹配使用re.sub()函式去除了n多複雜資訊,這點後面解釋。

4.輸出本地文字

為了防止程式碼執行錯誤,為了維護程式碼執行的和平,將輸出的資訊列表存入到excel檔案中了,方便日後查閱,很簡單的程式碼,需要了解pandas的用法。

def listToExcel(list,name):

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

    df.to_excel(name + '景點資訊.xlsx')

5.百度經緯度API

百度經緯度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)

在設定獲取經緯度的地址時,為了匹配到更準確的經緯度,我選擇了匹配景點地址,然而,景點地址裡有各種神奇的地址,因此有了第三部門中去除複雜的資訊。

然而,就算去掉了複雜資訊,還有一些匹配不到的景點地址,於是我使用了巢狀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哦~

640?wx_fmt=png

7.總結

因為註冊但沒有認證開發者賬號,所以每天只能獲取6K個經緯度API,所以我選擇了熱門景點中前400頁(每頁15個)的景點,結果可想而知。為了除錯因為資料增多出現的額外bug,最終的獲取的景點資料大概在4.5千條左右(爬取時間為2017年09月10日,爬取關鍵詞:熱門景點,僅代表當時銷量)。

640?wx_fmt=png

熱門景點熱力圖

640?wx_fmt=png

熱門景點示意圖

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

640?wx_fmt=jpeg

這樣的

640?wx_fmt=jpeg

還有這樣的

640?wx_fmt=jpeg

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

640?wx_fmt=png

熱門景點銷量top20

於是我又做了一個各城市包含熱門景點數目的排行,沒想到在4千多個熱門景點中,數目最多的竟是浙江,是第二個城市的1.5倍,而北京作為首都位居第二。

640?wx_fmt=png

主要城市熱門景點數

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

640?wx_fmt=png

主要城市熱門景點級別

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

640?wx_fmt=png

各省旅遊景點銷售起步價

到這裡已經分析完啦,最後祝大家國慶和中秋快樂!

0?wx_fmt=jpeg