高德POI的獲取
改進(jìn)代碼
該篇文章是關(guān)鍵詞搜索法獲取高德poi,但鑒于無(wú)法突破900條記錄的上限,因此重寫(xiě)了矩形搜索法的文章,具體可參考以下文章:
高德poi獲取之矩形搜索法(沖出900條限制)
(建議沒(méi)有python基礎(chǔ)的朋友先閱讀該篇再看矩形搜索法?。?/em>
前言
首先我們需要明白一些常識(shí)
- poi是興趣點(diǎn),它本身除了經(jīng)緯度,還記錄了一些信息,如名稱(chēng)、地址、聯(lián)系方式、所屬行政區(qū)
- 高德poi是指從高德地圖上獲取poi,所以我們借助的平臺(tái)是高德地圖
- 高德地圖知道有很多人需要用到poi,因此它十分良心地推出了若干個(gè)接口(API),借助這些API我們就可以各取所需。
- 最容易被忽略的一點(diǎn),通過(guò)高德地圖獲取得到的坐標(biāo)會(huì)有偏移,具體詳看3.2節(jié),因此我們獲取得到的坐標(biāo)還需要經(jīng)過(guò)坐標(biāo)系轉(zhuǎn)換才能得到正確的坐標(biāo),
API的理解
我們把整個(gè)poi的獲取理解成以下幾個(gè)步驟:
(1)找“高德地圖”這個(gè)人申請(qǐng)要數(shù)據(jù)
(2)高德地圖他說(shuō)你申請(qǐng)數(shù)據(jù)需要先填一下這張數(shù)據(jù)需求表
(3)你拿到這張表(API),開(kāi)始填寫(xiě)表(API)里的信息,包括POI的類(lèi)別,POI的區(qū)域,需要數(shù)據(jù)的格式
(4)高德地圖看了你填寫(xiě)的需求表,就從它的數(shù)據(jù)庫(kù)里找到并且交回給你
(5)你拿到數(shù)據(jù)之后,篩選出關(guān)心的信息,之后就找各種方式把數(shù)據(jù)保存起來(lái),例如存成一份excel表,或者一份txt文件,抑或是csv表格等等
所以通俗地講,API就是一份表,通過(guò)這個(gè)表可以讓對(duì)方返回你需要的東西,只不過(guò)實(shí)際上,這個(gè)表是用鏈接的形式發(fā)給你,在鏈接里填入信息就相當(dāng)于填表的行為了。
查看API接口
在高德開(kāi)放平臺(tái)上,我們獲取poi所需的接口位于[開(kāi)發(fā)支持]-[Web服務(wù)]-[Web服務(wù)API]下,點(diǎn)擊即可進(jìn)到主頁(yè)。我們需要用到的接口如下圖所示:
從圖上可以知道,高德地圖說(shuō)想使用這個(gè)API,你必須有我家的密鑰(key),因此我們首先需要去申請(qǐng)一下這個(gè)key。具體怎么申請(qǐng)可以參照以下這篇文章(https://zhuanlan.zhihu.com/p/96838566),接下來(lái)默認(rèn)大家都有這個(gè)key了。
然后呢,我們從適用場(chǎng)景那里得知,這個(gè)搜索poi有四種方法可以獲取poi,分別是關(guān)鍵字搜索、周邊搜索、多邊形搜索和ID查詢(xún),我們這里使用最簡(jiǎn)單的關(guān)鍵字搜索。
在關(guān)鍵字搜索下面,我們可以看到有個(gè)請(qǐng)求參數(shù)的表格,這個(gè)請(qǐng)求參數(shù)其實(shí)就是API里需要我們填寫(xiě)的信息,我們看到那么多參數(shù),其實(shí)不用慌,分清哪些是必填的,哪些是可填的,接下來(lái)就好辦了。
從我框選的地方可以知道,其實(shí)必填的只有兩三項(xiàng)。
1.key,就是我們上面申請(qǐng)的密鑰
2.keywords或者types,注意是兩者至少填一個(gè),我示例里選擇的是types
3.city,這個(gè)如果不填的話默認(rèn)是幫你搜索全國(guó)的,但我們一般都會(huì)指定一個(gè)區(qū)域
4.page,你可以想象成一頁(yè)頁(yè)的紙,高德地圖默認(rèn)每頁(yè)20條信息,默認(rèn)返回一頁(yè)給你,也就是20條。那么為了獲取全部的poi,我們需要高德地圖返回盡可能多頁(yè)給我們,直到搜不到poi為止,因此這個(gè)參數(shù)在控制循環(huán)的時(shí)候尤為重要
5.output,返回的數(shù)據(jù)格式類(lèi)型,我們一般都返回JSON格式,方便我們處理
接下來(lái),我們查看一下返回的參數(shù),這部分內(nèi)容比較多,因此我錄了個(gè)視頻來(lái)講解。
核心參數(shù):
1. status
2. pois(核心參數(shù):location、name等)
查看完返回結(jié)果參數(shù)之后,我們就已經(jīng)明白了我們需要填寫(xiě)的信息和最后得到的信息有哪些,那么距離開(kāi)始操作就只差最后一步了,就是拿表(API)填寫(xiě)。
高德地圖里給我們舉了個(gè)栗子:
不難發(fā)現(xiàn),這個(gè)API里面有一些參數(shù)是我們熟悉的,例如keywords、city、key,并且這些關(guān)鍵詞都是通過(guò)“&”符號(hào)進(jìn)行連接,因此我們只要能夠替換掉它的這些信息,就可以完成API的構(gòu)建啦。
例如我需要獲取東莞市的,就把 city=beijing
改成city = 東莞市
需要獲取公園類(lèi)型的poi,就把keywords換成types,然后types = 公園
即可。
實(shí)現(xiàn)思路
這是我的一些想法:
-
我們現(xiàn)在已經(jīng)學(xué)會(huì)怎么通過(guò)修改鏈接里的關(guān)鍵詞來(lái)構(gòu)建我們所需的API,那么下一步其實(shí)只需要向高德地圖發(fā)出申請(qǐng)即可
-
但仔細(xì)思考,我們可以把整個(gè)過(guò)程劃分成以下三個(gè)部分:
- 發(fā)出一次申請(qǐng),高德地圖返回一頁(yè)數(shù)據(jù)給我,我們可以將這一次的申請(qǐng)想象成舉手向老師拿東西(但注意,老師不會(huì)一次性把所有東西都給你)
- 我們通過(guò)某個(gè)指標(biāo),保證區(qū)域內(nèi)的poi都獲取完,這個(gè)過(guò)程理解為如果老師沒(méi)有把東西全都給我,那我就一直舉手,直到給完為止。
- 獲取完之后我們需要將數(shù)據(jù)存到電腦上,因此這個(gè)過(guò)程需要我們想方設(shè)法地存成某種文件。
-
通過(guò)上面過(guò)程的劃分,我們可以抽象成三個(gè)函數(shù),一個(gè)舉手函數(shù),一個(gè)反復(fù)舉手函數(shù),一個(gè)保存結(jié)果函數(shù)。
-
雖然我在這次實(shí)驗(yàn)中需要獲取高德地圖poi,但如果下次我需要獲取另外一個(gè)地方的其他類(lèi)型poi,我能否用這次的方法來(lái)直接獲取而不是重新寫(xiě)一次代碼?
-
因此我想將上述的過(guò)程都封裝成函數(shù),下次再使用的時(shí)候只需要改改地區(qū)、poi類(lèi)別即可完成所有工作。
實(shí)現(xiàn)過(guò)程
這里我們引入所需要的庫(kù)
#庫(kù)的說(shuō)明
1.requests --爬蟲(chóng)常用庫(kù)
2.json --該庫(kù)能夠幫我們處理高德返回的JSON格式的數(shù)據(jù)
3.xlwt --名字很奇怪,但它是xlsx、xls writer的意思,就是存儲(chǔ)為excel表格庫(kù)
4.Coordin_transformlat -- 這個(gè)是自己寫(xiě)的一個(gè)坐標(biāo)轉(zhuǎn)換的庫(kù),其目的是為了解決開(kāi)頭說(shuō)的坐標(biāo)偏移的問(wèn)題
5.urlib -- 主要使用該庫(kù)里的quote函數(shù),因?yàn)楫?dāng)我們向電腦輸入漢字時(shí),計(jì)算機(jī)其實(shí)并不認(rèn)識(shí)漢字,因?yàn)樗徽J(rèn)識(shí)0101這些二進(jìn)制代碼,那么quote函數(shù)就可以將漢字翻譯成計(jì)算機(jī)能夠懂的語(yǔ)言。
#引入所需的庫(kù)
import requests
import json
from urllib.parse import quote
import xlwt
from Coordin_transformlat import gcj02towgs84
1.構(gòu)建一個(gè)申請(qǐng)的函數(shù)(舉手函數(shù))
回想一下剛剛的api,申請(qǐng)必須填寫(xiě)的幾個(gè)參數(shù):key,city,types或keywords,同時(shí),容易忽略的一個(gè)參數(shù)是page,在請(qǐng)求函數(shù)里page是個(gè)可選參數(shù),表示當(dāng)前頁(yè)數(shù),我們可以通過(guò)遞增page的值來(lái)反復(fù)地提交申請(qǐng),例如第一次先申請(qǐng)第一頁(yè),第二次申請(qǐng)第二頁(yè)這樣實(shí)現(xiàn),所以這里至少需要四個(gè)參數(shù)
代碼理解
url: 是一個(gè)鏈接,通過(guò)訪問(wèn)這個(gè)鏈接,我們可以獲取得到所需的數(shù)據(jù)
User-Agent:用戶(hù)代理頭,因?yàn)槲覀兙帉?xiě)的是爬蟲(chóng)程序,通過(guò)爬蟲(chóng)去獲取數(shù)據(jù),而網(wǎng)站其實(shí)是不太支持爬蟲(chóng)爬取數(shù)據(jù),因?yàn)榕老x(chóng)的訪問(wèn)次數(shù)太快太多了,后臺(tái)容易崩潰。那怎么讓對(duì)方認(rèn)不出來(lái)這是個(gè)爬蟲(chóng)呢?
通過(guò)添加User-Agent來(lái)給爬蟲(chóng)戴上用戶(hù)(指正常瀏覽網(wǎng)站的人)的面具,這樣瞞過(guò)對(duì)方視線,一般設(shè)置在headers里。
request.get():我們通過(guò)get(url,header)來(lái)爬取數(shù)據(jù)
get()方法,只要你輸入一個(gè)網(wǎng)址就可以模擬人瀏覽網(wǎng)站的行為,相當(dāng)于訪問(wèn)url這個(gè)鏈接
def Get_poi(key,city,types,page):
'''
這是一個(gè)能夠從高德地圖獲取poi數(shù)據(jù)的函數(shù)
key:為用戶(hù)申請(qǐng)的高德密鑰
city:目標(biāo)城市
types:POI數(shù)據(jù)的類(lèi)型
page:當(dāng)前頁(yè)數(shù)
'''
#設(shè)置header
header = {'User-Agent': "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50"}
#構(gòu)建url
#{}在鏈接里表示占位符,也就是占住位置先不填寫(xiě),.format()里就是往剛剛占位符的地方填寫(xiě)變量,按照順序一一對(duì)應(yīng),第一個(gè){}里是key,第二個(gè){}里是types
url = 'https://restapi.amap.com/v3/place/text?key={}&types={}&city={}&page={}&output=josn'.format(key,types,quote(city),page)
#用get函數(shù)請(qǐng)求數(shù)據(jù)
r = requests.get(url,headers=header)
#設(shè)置數(shù)據(jù)的編碼為'utf-8'
r.encoding = 'utf-8'
#將請(qǐng)求得到的數(shù)據(jù)按照'utf-8'編碼成字符串
data = r.text
return data
2.構(gòu)建反復(fù)申請(qǐng)的函數(shù)(多次舉手)
這里只需要重復(fù)調(diào)用上面構(gòu)造好的申請(qǐng)函數(shù),直到最后一頁(yè)的數(shù)據(jù)條數(shù)(Count)為0
因此這里只要當(dāng)前頁(yè)數(shù)(page)的記錄條數(shù)(count)不為0,就一直執(zhí)行循環(huán)
def Get_times(key,city,types):
'''
這是一個(gè)控制申請(qǐng)次數(shù)的函數(shù)
'''
page = 1
#創(chuàng)建一個(gè)poilist的空列表
poilist = []
#執(zhí)行以下代碼,直到count為0的時(shí)候跳出循環(huán)
while True:
#調(diào)用第一個(gè)函數(shù)來(lái)獲取數(shù)據(jù)
result = Get_poi(key,city,types,page)
#json.loads可以對(duì)獲取回來(lái)JSON格式的數(shù)據(jù)進(jìn)行解碼
content = json.loads(result)
#content的樣子其實(shí)跟返回結(jié)果參數(shù)是一樣的,可以想象成有很多個(gè)字段的excel表格,下面這個(gè)語(yǔ)句就是提取出pois那個(gè)字段
pois = content['pois']
#pois的信息寫(xiě)入空列表里,這里由于不知道返回的數(shù)據(jù)長(zhǎng)什么樣子,所以會(huì)難以理解些
for i in range(len(pois)):
poilist.append(pois[i])
#遞增page
page = page + 1
#判斷當(dāng)前頁(yè)下的count是否等于0
if content['count'] == '0':
break
#將寫(xiě)好poi信息的列表返回
return poilist
content的樣子:
這圖里,我們可以看到有很多條數(shù)據(jù),這些數(shù)據(jù)就是高德地圖返回給我們的,但類(lèi)似于info、suggestion這些字段我們是不關(guān)心的,我們只關(guān)心pois這個(gè)字段,所以我們用pois = content['pois']
這行代碼來(lái)提取出pois這個(gè)字段。
但如果觀察得仔細(xì),你會(huì)發(fā)現(xiàn)pois字段里面還有很多的信息,除了我們關(guān)心的location、name以外,還有id、parent、email等,所以第三步需要我們?cè)僖淮魏Y選信息
3.構(gòu)建保存函數(shù)
通過(guò)上述的函數(shù),我們得到了只包含pois字段的列表,但這個(gè)pois字段里還有很多無(wú)關(guān)的信息,因此我們這里單獨(dú)提取出經(jīng)緯度(location)、名稱(chēng)(name)、地址(address)
(下面這段代碼是直接搬了別人的過(guò)來(lái),現(xiàn)成的就不用自己再寫(xiě)一遍了,意義不大)
借鑒博客:(https://blog.csdn.net/john_ashley/article/details/114196683)
def write_to_excel(poilist, city,types):
'''
這是一個(gè)可以將列表寫(xiě)入excel的函數(shù)
poilist -- 上面得到的poilist
city -- 城市名,這個(gè)參數(shù)是保存excel文件的名字用的
types -- poi類(lèi)別,這個(gè)也是為了保存excel文件,可直接看代碼最后一行
'''
#我們可以把這兩行代碼理解成新建一個(gè)excel表,第一句是新建excel文件
book = xlwt.Workbook(encoding='utf-8', style_compression=0)
#往這個(gè)excel文件新建一個(gè)sheet表格
sheet = book.add_sheet(types, cell_overwrite_ok=True)
# 第一行(列標(biāo)題)
sheet.write(0, 0, 'x')
sheet.write(0, 1, 'y')
sheet.write(0, 2, 'count')
sheet.write(0, 3, 'name')
sheet.write(0, 4, 'address')
sheet.write(0, 5, 'adname')
#最難理解的地方應(yīng)該是這里了,放到代碼后面講解
for i in range(len(poilist)):
name = poilist[i]['name']
location = poilist[i]['location']
address = poilist[i]['address']
adname = poilist[i]['adname']
lng = str(location).split(",")[0]
lat = str(location).split(",")[1]
#這里是坐標(biāo)系轉(zhuǎn)換,也放到代碼后面詳解
result = gcj02towgs84(location)
lng = result[0]
lat = result[1]
# 每一行寫(xiě)入
sheet.write(i + 1, 0, lng)
sheet.write(i + 1, 1, lat)
sheet.write(i + 1, 2, 1)
sheet.write(i + 1, 3, name)
sheet.write(i + 1, 4, address)
sheet.write(i + 1, 5, adname)
# 最后,將以上操作保存到指定的Excel文件中
book.save(city + "_" + types + '.xls')
第三個(gè)文件其實(shí)難以理解的地方只有for循環(huán)跟坐標(biāo)系轉(zhuǎn)換,第一個(gè)是技術(shù)問(wèn)題,第二個(gè)是背景問(wèn)題,我們先來(lái)將坐標(biāo)系。
3.1 高德的坐標(biāo)系
1.為什么高德地圖得到的坐標(biāo)系不能直接使用?
其實(shí)是因?yàn)閲?guó)家規(guī)定,中國(guó)大陸所有公開(kāi)地理數(shù)據(jù)都需要至少用GCJ-02進(jìn)行加密,也就是說(shuō)我們從國(guó)內(nèi)公司的產(chǎn)品中得到的數(shù)據(jù),一定是經(jīng)過(guò)了加密的。絕大部分國(guó)內(nèi)互聯(lián)網(wǎng)地圖提供商都是使用GCJ-02坐標(biāo)系,包括高德地圖,谷歌地圖中國(guó)區(qū)等。
2.關(guān)于這個(gè)GCJ-02坐標(biāo)系
GCJ-02(G-Guojia國(guó)家,C-Cehui測(cè)繪,J-Ju局),又被稱(chēng)為火星坐標(biāo)系,是一種基于WGS-84制定的大地測(cè)量系統(tǒng),由中國(guó)國(guó)測(cè)局制定。此坐標(biāo)系所采用的混淆算法會(huì)在經(jīng)緯度中加入隨機(jī)的偏移。所以這就是我們?yōu)槭裁葱枰獙?duì)獲取得到的經(jīng)緯度進(jìn)行坐標(biāo)系轉(zhuǎn)換的原因了。
3.“Coordin_transformlat – 這個(gè)是自己寫(xiě)的一個(gè)坐標(biāo)轉(zhuǎn)換的庫(kù)” 這個(gè)怎么理解呢?
其實(shí)呢,就是自己寫(xiě)了一個(gè)python文件,只不過(guò)在這個(gè)文件里有一些方法,我希望在另外的文件里也能用上,所以通過(guò)import的方法引入,例如我在A文件里寫(xiě)了某個(gè)方法,然后我在B文件里也想使用這個(gè)方法,那么我在B文件里import A即可。
當(dāng)B文件運(yùn)行的時(shí)候,系統(tǒng)讀到了這個(gè)方法,就會(huì)去A文件里看看這個(gè)方法是怎樣的,大致如此。
4.Coordin_transformlat這個(gè)模塊有什么用呢?
從上面標(biāo)注其實(shí)可以發(fā)現(xiàn),這個(gè)模塊里頭有許多函數(shù),如bd09todcj02,gcj02tobd09,wgs84togcj02等等,其實(shí)這些都是坐標(biāo)轉(zhuǎn)換的函數(shù),目的不就是實(shí)現(xiàn)坐標(biāo)轉(zhuǎn)換嗎?所以在代碼中的那句result = gcj02towgs84(location)
就不難理解了,是從火星坐標(biāo)系(gcj02)轉(zhuǎn)向WGS84坐標(biāo)系。
Coordin_transformlat源代碼
# * 百度坐標(biāo)系 (BD-09) 與 火星坐標(biāo)系 (GCJ-02)的轉(zhuǎn)換
# * 即 百度 轉(zhuǎn) 谷歌、高德
# * @param bd_lon
# * @param bd_lat
# * @returns {*[]}
# */
import math
def bd09togcj02(bd_lon, bd_lat):
x_pi = 3.14159265358979324 * 3000.0 / 180.0
x = bd_lon - 0.0065
y = bd_lat - 0.006
z = math.sqrt(x * x + y * y) - 0.00002 * math.sin(y * x_pi)
theta = math.atan2(y, x) - 0.000003 * math.cos(x * x_pi)
gg_lng = z * math.cos(theta)
gg_lat = z * math.sin(theta)
return [gg_lng, gg_lat]
# * 火星坐標(biāo)系 (GCJ-02) 與百度坐標(biāo)系 (BD-09) 的轉(zhuǎn)換
# * 即谷歌、高德 轉(zhuǎn) 百度
# */
def gcj02tobd09(lng, lat):
x_PI = 3.14159265358979324 * 3000.0 / 180.0
z = math.sqrt(lng * lng + lat * lat) + 0.00002 * math.sin(lat * x_PI)
theta = math.atan2(lat, lng) + 0.000003 * math.cos(lng * x_PI)
bd_lng = z * math.cos(theta) + 0.0065
bd_lat = z * math.sin(theta) + 0.006
return [bd_lng, bd_lat]
# wgs84轉(zhuǎn)高德
def wgs84togcj02(lng, lat):
PI = 3.1415926535897932384626
ee = 0.00669342162296594323
a = 6378245.0
dlat = transformlat(lng - 105.0, lat - 35.0)
dlng = transformlng(lng - 105.0, lat - 35.0)
radlat = lat / 180.0 * PI
magic = math.sin(radlat)
magic = 1 - ee * magic * magic
sqrtmagic = math.sqrt(magic)
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI)
dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * PI)
mglat = lat + dlat
mglng = lng + dlng
return [mglng, mglat]
# GCJ02/谷歌、高德 轉(zhuǎn)換為 WGS84 gcj02towgs84
def gcj02towgs84(localStr):
lng = float(localStr.split(',')[0])
lat = float(localStr.split(',')[1])
PI = 3.1415926535897932384626
ee = 0.00669342162296594323
a = 6378245.0
dlat = transformlat(lng - 105.0, lat - 35.0)
dlng = transformlng(lng - 105.0, lat - 35.0)
radlat = lat / 180.0 * PI
magic = math.sin(radlat)
magic = 1 - ee * magic * magic
sqrtmagic = math.sqrt(magic)
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI)
dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * PI)
mglat = lat + dlat
mglng = lng + dlng
return [lng * 2 - mglng,lat * 2 - mglat]
def transformlat(lng, lat):
PI = 3.1415926535897932384626
ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * \
lat + 0.1 * lng * lat + 0.2 * math.sqrt(abs(lng))
ret += (20.0 * math.sin(6.0 * lng * PI) + 20.0 *
math.sin(2.0 * lng * PI)) * 2.0 / 3.0
ret += (20.0 * math.sin(lat * PI) + 40.0 *
math.sin(lat / 3.0 * PI)) * 2.0 / 3.0
ret += (160.0 * math.sin(lat / 12.0 * PI) + 320 *
math.sin(lat * PI / 30.0)) * 2.0 / 3.0
return ret
def transformlng(lng, lat):
PI = 3.1415926535897932384626
ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + \
0.1 * lng * lat + 0.1 * math.sqrt(abs(lng))
ret += (20.0 * math.sin(6.0 * lng * PI) + 20.0 *
math.sin(2.0 * lng * PI)) * 2.0 / 3.0
ret += (20.0 * math.sin(lng * PI) + 40.0 *
math.sin(lng / 3.0 * PI)) * 2.0 / 3.0
ret += (150.0 * math.sin(lng / 12.0 * PI) + 300.0 *
math.sin(lng / 30.0 * PI)) * 2.0 / 3.0
return ret
3.2 循環(huán)體代碼的理解
for i in range(len(poilist)):
name = poilist[i]['name']
location = poilist[i]['location']
address = poilist[i]['address']
adname = poilist[i]['adname']
lng = str(location).split(",")[0]
lat = str(location).split(",")[1]
如果你想理解上面的循環(huán)體,那么必須要了解以下的知識(shí):
- poilist是長(zhǎng)什么樣的
- for循環(huán)里的i
- 多維列表的遍歷。
那么下面我就一一講解一下。
poilist的樣子大概是這樣的:
這里需要注意的是,poilist它本身是一個(gè)列表,這個(gè)大列表里面包含著295個(gè)子項(xiàng),每一個(gè)子項(xiàng)又是字典形式的。
畫(huà)出示意圖大概就是下面這樣:
name = poilist[i]['name']
location = poilist[i]['location']
address = poilist[i]['address']
adname = poilist[i]['adname']
所以你再看這幾行代碼的時(shí)候,是不是就沒(méi)那么陌生了
而在for循環(huán)里的i,它其實(shí)是個(gè)變量。
你可以想象一下"有十只羊,按順序排好隊(duì),一只只地送上屠宰場(chǎng)"這個(gè)情景,我們假設(shè)一個(gè)準(zhǔn)備送上屠宰場(chǎng)的羊 的變量,那么隨著羊一只只地走了,這個(gè)變量也在不斷地指向下一只羊,它所指代的內(nèi)容是不斷變化的。
再比如,在for i in range(1,10)
這個(gè)代碼里,第一次循環(huán)i是1,然后下一次循環(huán)的時(shí)候i就變成了2,下一次變成了3,只到所有的都輪過(guò)一遍才結(jié)束。
因此,不難理解,在我們的代碼 for i in range(len(poilist)):
里,i是一個(gè)變化著的數(shù)值,從0開(kāi)始,直到變成poilist的長(zhǎng)度那么大的數(shù)字結(jié)束。然后剛好這個(gè)數(shù)字放在了poilist[i]里,可以不斷地指向poilist的每個(gè)子項(xiàng)。
完整代碼:
import requests
import json
from urllib.parse import quote
import xlwt
from Coordin_transformlat import gcj02towgs84
def Get_poi(key, city, types, page):
'''
這是一個(gè)能夠從高德地圖獲取poi數(shù)據(jù)的函數(shù)
key:為用戶(hù)申請(qǐng)的高德密鑰
city:目標(biāo)城市
types:POI數(shù)據(jù)的類(lèi)型
page:當(dāng)前頁(yè)數(shù)
'''
# 設(shè)置header
header = {
'User-Agent': "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50"}
# 構(gòu)建url
# {}在鏈接里表示占位符,也就是占住位置先不填寫(xiě),.format()里就是往剛剛占位符的地方填寫(xiě)變量,按照順序一一對(duì)應(yīng),第一個(gè){}里是key,第二個(gè){}里是types
url = 'https://restapi.amap.com/v3/place/text?key={}&types={}&city={}&page={}&output=josn'.format(key, types,
quote(city), page)
# 用get函數(shù)請(qǐng)求數(shù)據(jù)
r = requests.get(url, headers=header)
# 設(shè)置數(shù)據(jù)的編碼為'utf-8'
r.encoding = 'utf-8'
# 將請(qǐng)求得到的數(shù)據(jù)按照'utf-8'編碼成字符串
data = r.text
return data
def Get_times(key, city, types):
'''
這是一個(gè)控制申請(qǐng)次數(shù)的函數(shù)
'''
page = 1
# 創(chuàng)建一個(gè)poilist的空列表
poilist = []
# 執(zhí)行以下代碼,直到count為0的時(shí)候跳出循環(huán)
while True:
# 調(diào)用第一個(gè)函數(shù)來(lái)獲取數(shù)據(jù)
result = Get_poi(key, city, types, page)
# json.loads可以對(duì)獲取回來(lái)JSON格式的數(shù)據(jù)進(jìn)行解碼
content = json.loads(result)
# content的樣子其實(shí)跟返回結(jié)果參數(shù)是一樣的,可以想象成有很多個(gè)字段的excel表格,下面這個(gè)語(yǔ)句就是提取出pois那個(gè)字段
pois = content['pois']
# pois的信息寫(xiě)入空列表里,這里由于不知道返回的數(shù)據(jù)長(zhǎng)什么樣子,所以會(huì)難以理解些
for i in range(len(pois)):
poilist.append(pois[i])
# 遞增page
page = page + 1
# 判斷當(dāng)前頁(yè)下的count是否等于0
if content['count'] == '0':
break
# 將寫(xiě)好poi信息的列表返回
return poilist
def write_to_excel(poilist, city,types):
'''
這是一個(gè)可以將列表寫(xiě)入excel的函數(shù)
poilist -- 上面得到的poilist
city -- 城市名,這個(gè)參數(shù)是保存excel文件的名字用的
types -- poi類(lèi)別,這個(gè)也是為了保存excel文件,可直接看代碼最后一行
'''
#我們可以把這兩行代碼理解成新建一個(gè)excel表,第一句是新建excel文件
book = xlwt.Workbook(encoding='utf-8', style_compression=0)
#往這個(gè)excel文件新建一個(gè)sheet表格
sheet = book.add_sheet(types, cell_overwrite_ok=True)
# 第一行(列標(biāo)題)
sheet.write(0, 0, 'x')
sheet.write(0, 1, 'y')
sheet.write(0, 2, 'count')
sheet.write(0, 3, 'name')
sheet.write(0, 4, 'address')
sheet.write(0, 5, 'adname')
#最難理解的地方應(yīng)該是這里了,放到代碼后面講解
for i in range(len(poilist)):
name = poilist[i]['name']
location = poilist[i]['location']
address = poilist[i]['address']
adname = poilist[i]['adname']
lng = str(location).split(",")[0]
lat = str(location).split(",")[1]
#這里是坐標(biāo)系轉(zhuǎn)換,也放到代碼后面詳解
result = gcj02towgs84(location)
lng = result[0]
lat = result[1]
# 每一行寫(xiě)入
sheet.write(i + 1, 0, lng)
sheet.write(i + 1, 1, lat)
sheet.write(i + 1, 2, 1)
sheet.write(i + 1, 3, name)
sheet.write(i + 1, 4, address)
sheet.write(i + 1, 5, adname)
# 最后,將以上操作保存到指定的Excel文件中
book.save(city + "_" + types + '.xls')
#這里修改為自己的高德密鑰
key = '**********'
#這里修改自己的poi類(lèi)型
types = ['公園廣場(chǎng)','汽車(chē)服務(wù)']
#建議將大區(qū)域切分成幾個(gè)小區(qū)域來(lái)獲取,保證獲取的數(shù)據(jù)齊全
city_list = ['白云區(qū)','天河區(qū)','越秀區(qū)','黃埔區(qū)']
#先遍歷city_list里面的每個(gè)區(qū)域
for city in city_list:
#再遍歷types里的每個(gè)類(lèi)別
for type in types:
poi = Get_times(key,city,type)
print('當(dāng)前城市:' + str(city) + ', 分類(lèi):' + str(type) + ", 總的有" + str(len(poi)) + "條數(shù)據(jù)")
write_to_excel(poi,city,type)
print('*'*50+'分類(lèi):' + str(type) + "寫(xiě)入成功"+'*'*50)
print('====爬取完成====')
運(yùn)行結(jié)果
汽車(chē)服務(wù)的有幾條數(shù)據(jù)都是897,應(yīng)該是區(qū)域內(nèi)的poi數(shù)量超過(guò)1000了,因此這個(gè)通過(guò)關(guān)鍵字搜索poi的方法還有待改善~碼不動(dòng)了,改日再戰(zhàn)!
總的來(lái)說(shuō),過(guò)程還是挺順利的!文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-460653.html
一些關(guān)鍵詞搜索法的問(wèn)題
在2022年7月6日高德POI進(jìn)行了一次更新之后,關(guān)鍵詞搜索法也出現(xiàn)了一些新的問(wèn)題,例如獲取到的POI數(shù)量最多為200條,這里我也嘗試獲取某個(gè)區(qū)的酒店poi,發(fā)現(xiàn)從11頁(yè)開(kāi)始POI的數(shù)量就變?yōu)?了。
這個(gè)問(wèn)題也困擾著我,暫時(shí)無(wú)法用該方法解決。所幸的是矩形搜索法目前能夠很好地解決這個(gè)問(wèn)題,所以建議大家將該方法作為熟悉高德POI的一種方式,實(shí)戰(zhàn)中用矩形搜索法去獲取齊全的POI數(shù)據(jù)。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-460653.html
到了這里,關(guān)于Python獲取高德POI(關(guān)鍵詞搜索法)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!