使用高德開(kāi)放平臺(tái)api批量爬取所需經(jīng)緯度及位置信息(平臺(tái)教程和python多進(jìn)程、多線程代碼詳解)

2019.3.20更新(將代碼升級(jí)為非阻塞式多進(jìn)程,效率極大提升)
2019.6.28更新 (將代碼模塊化,復(fù)用性更強(qiáng),使用更高效的線程池進(jìn)行爬?。?/strong>

  • 之前寫(xiě)爬蟲(chóng)對(duì)鏈家某地區(qū)全部二手房信息進(jìn)行了獲取并存在了MongoDB數(shù)據(jù)庫(kù)。進(jìn)行數(shù)據(jù)可視化時(shí),想要做基于地圖信息的分析,可所獲信息中未包含經(jīng)緯度值。

  • 經(jīng)過(guò)搜索大法,發(fā)現(xiàn)原來(lái)百度地圖和高德地圖都有開(kāi)放平臺(tái),可基于文本位置返回更詳細(xì)的地理位置信息。以高德開(kāi)發(fā)平臺(tái)為例,具體過(guò)程如下:

  • 直達(dá)鏈接:高德開(kāi)放平臺(tái)

  • 高德開(kāi)放平臺(tái)提供的接口功能很多,這里只是使用"地理/逆地理編碼功能",將結(jié)構(gòu)化地址在經(jīng)緯度之間互轉(zhuǎn)。這里,需要先注冊(cè)開(kāi)發(fā)者賬號(hào)并申請(qǐng)一個(gè)key(注冊(cè)后需先隨便創(chuàng)建一個(gè)應(yīng)用)。


    開(kāi)發(fā)指南頁(yè)面
  • 注冊(cè)賬號(hào),并創(chuàng)建了一個(gè)應(yīng)用,獲得一個(gè)key密匙。


    獲得key密匙
  • 然后就可以愉快的使用api獲得經(jīng)緯度啦!
    api請(qǐng)求鏈接為示例:
    "https://restapi.amap.com/v3/geocode/geo?address=北京市朝陽(yáng)區(qū)阜通東大街6號(hào)&output=XML&key=<用戶(hù)的key>"

  • 這里只需要替換address為你需要查詢(xún)的地點(diǎn)、替換key為你的key就可以了,我們?cè)跒g覽器中做個(gè)試驗(yàn):


    api信息返回

    可以看到調(diào)用高德的api接口時(shí)能獲得更完整的地理位置信息以及經(jīng)緯度志,親測(cè)百度開(kāi)放平臺(tái)的api不會(huì)返回更詳細(xì)地理信息,但可以返回該位置的類(lèi)型,比如“小區(qū)”之類(lèi)的??梢暻闆r選擇。

很重要的一點(diǎn)差點(diǎn)忘了,不要以為這可以隨便嗨了,免費(fèi)用戶(hù)的每日調(diào)用次數(shù)和每秒并發(fā)量是有限制的!
每日調(diào)用限制

百度地圖和高德地圖的每日限量都是相同的,你也可以同時(shí)注冊(cè)兩邊的賬號(hào),就像我一樣,不過(guò)對(duì)一般需求來(lái)說(shuō)也已經(jīng)足夠了。

  • 下面直接上代碼,用數(shù)據(jù)庫(kù)中的小區(qū)名字來(lái)批量獲取經(jīng)緯度和更詳細(xì)的地理信息,邏輯很簡(jiǎn)單,傳入小區(qū)名、解析請(qǐng)求就好了:
import pymongo
import pandas as pd
import requests
import re
from multiprocessing import Pool
#數(shù)據(jù)庫(kù)連接
client = pymongo.MongoClient("localhost",27017)
db = client['ershoufang']
collection = db["lianjia_solded"]
location = db['locations']

#高德地圖獲取地理信息的api接口
gaode_api_url = "https://restapi.amap.com/v3/geocode/geo?address={}&output=XML&key=YOUR KEY"
headers = {
    "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"
}

def get_location_info(loc):
    """
    利用高德開(kāi)放平臺(tái),解析小區(qū)全部位置信息(包含經(jīng)緯度),存入數(shù)據(jù)庫(kù)
    :param loc: 小區(qū)名字
    :return: 具體位置信息
    """
    new_loc = "成都市" + loc
    parse_adress_url = gaode_api_url.format(new_loc)
    response = requests.get(parse_adress_url, headers=headers).text
    # 加入判斷防止空白信息返回
    if re.search(r"<count>1</count>", response, re.S):
        # 使用正則表達(dá)式提取api反饋的地理信息
        detail_info = re.findall(r"_address>(.*?)</.*?<district>(.*?)</district>.*?<location>(.*?)</location>", response, re.S)[0]
        result = {
            'house_name': loc,
            'adress': detail_info[0],
            'district': detail_info[1],
            'location': detail_info[2],
            'longitude': detail_info[2].split(",")[0],
            'latitude': detail_info[2].split(",")[1]
        }
        # 插入數(shù)據(jù)庫(kù)
        location.insert_one(result)
        print(result)
    else:
        print("Something Wrong!未獲取到api信息!")


if __name__ == '__main__':
    #從數(shù)據(jù)庫(kù)中獲取源小區(qū)名
    data = pd.DataFrame(list(collection.find())).drop(['elevator', 'url', 'village_id'], axis='columns')
    # 小區(qū)名
    locs = data["village_name"]
    locs_num = pd.value_counts(locs, sort=True)
    #開(kāi)啟進(jìn)程池
    p = Pool()
    for loc in locs_num.index[:6000]:  # 高德api限制每天請(qǐng)求不超過(guò)6000個(gè)
        p.apply_async(get_location_info, (loc,))
    p.close()
    p.join()

6.28更新代碼

import pymongo
import pandas as pd
import requests
import re
from concurrent import futures
from logging import warning

class GaodeLocation(object):
    # 初始化連接到Mongo數(shù)據(jù)庫(kù)
    def __init__(self, key, city):
        '''
        初始化連接到數(shù)據(jù)庫(kù)
            :param key: 高德開(kāi)放平臺(tái)提供的KEY,需攜帶才能訪問(wèn)
            :param db: 數(shù)據(jù)庫(kù)名
            :param collectin: 存放小區(qū)數(shù)據(jù)的表名
            :param loc_collection: 存放位置信息的新表名
        '''
        self.CITY = city
        self.gaode_api_url = "https://restapi.amap.com/v3/geocode/geo?address={}&output=XML&key=" + key

        self.client = pymongo.MongoClient("localhost", 27017)
        self.db = self.client["ershoufang"]
        self.collection = self.db["lianjia_solded"]
        self.loc_collection = self.db['locations']

    # 將經(jīng)緯度信息存入數(shù)據(jù)庫(kù)
    def to_database(self,result):
        return  self.loc_collection.insert_one(result)
    # 傳入位置字符串,通過(guò)高德API獲取經(jīng)緯度信息
    def request_info(self,loc):
        detail_loc = CITY + loc
        parse_adress_url = self.gaode_api_url.format(detail_loc)
        response = requests.get(parse_adress_url).text
        # 加入判斷防止空白信息返回
        if re.search(r"<count>1</count>", response, re.S):
            # 提取api反饋的地理信息
            detail_info = re.findall(r"_address>(.*?)</.*?<district>(.*?)</district>.*?<location>(.*?)</location>", response,re.S)[0]
            result = {
                'house_name': loc,
                'adress': detail_info[0],
                'district': detail_info[1],
                'location': detail_info[2],
                'longitude': detail_info[2].split(",")[0],
                'latitude': detail_info[2].split(",")[1]
            }
            print(result)
            self.to_database(result)
        else:
            warning("{}位置信息未成功獲取".format(loc))
            return None

    def main(self):
        # 從數(shù)據(jù)庫(kù)中獲取源小區(qū)名
        data = pd.DataFrame(list(self.collection.find())).drop(['elevator', 'url', 'village_id'], axis='columns')
        # 小區(qū)名字段
        locs = data["village_name"]
        # 按小區(qū)名出現(xiàn)頻率排序
        locs_num = pd.value_counts(locs, sort=True)
        # 高德開(kāi)放平臺(tái)一天只允許免費(fèi)用戶(hù)使用API接口6000次......
        available_loc_list = locs_num.index[:6000]

        with futures.ThreadPoolExecutor(max_workers=20) as excutor:
            excutor.map(self.request_info, available_loc_list)

if __name__ == '__main__':
    KEY = "c9ac8XXXXXXXXXXXXXXXXXXX"  # 你的KEY
    CITY = "成都市"    # 你的城市
    s = GaodeLocation(KEY, city=CITY)
    s.main()

模塊化代碼復(fù)用性更強(qiáng),使用線程池進(jìn)行批量IO操作,效率進(jìn)一步提升,

OK,大功告成啦,6000條位置數(shù)據(jù)大概就幾分鐘吧!


存入數(shù)據(jù)庫(kù)中的信息
多進(jìn)程爬取數(shù)度很快

完。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容