經(jīng)緯度地址轉(zhuǎn)換的方法集合(Python描述)

Python 2.7
IDE Pycharm 5.0.3
Geopy 1.11


前言

這只是我想做的一部分,寫一塊太大了,單獨(dú)記錄

目的

獲取2015年GDP TOP100城市并獲取城市對(duì)應(yīng)經(jīng)緯度,存入txt后續(xù)操作

獲取GDP TOP100城市

查詢網(wǎng)址如下http://www.redsh.com/a/20160126/171501.shtml
2015中國100大城市GDP排行榜TOP100出爐!

頁面的效果大概是這樣的~慘不忍睹的格式,花了我好多時(shí)間提取。。。。

元素大概是這樣的

ok,先把TOP100城市爬下來再說


方法1:靜態(tài)爬取

采用urllib2+BS4+re(更多靜態(tài)爬取方法點(diǎn)擊這里),當(dāng)然你也可以采用request來代替urllib2,這里我只做個(gè)試驗(yàn),所以用了urllib2,廢話不多說,直接上代碼!

# 采用urllib2+BS4進(jìn)行靜態(tài)網(wǎng)頁解析
def getCityByBS4(url):
    
    html = urllib2.urlopen(urllib2.Request(url)).read().decode('gbk')
    soup = BeautifulSoup(html,'lxml')
    x = soup.find_all('p')
    t=1
    cityLocationList = []
    cityLocationListOnlyLatLng =[]
    for i in x:
        if t > 5 and t<106:
            city1 = i.get_text()
            city1 = city1.encode('utf-8')
            # 數(shù)據(jù)清洗過程
            city1 = re.split('\.',city1)[1]
            city1 = re.split('(',city1)[0]
            if len(city1)>13:
                city2 = city1[0:6]
            else:
                city2 =city1.strip()
            try:
                lat_lng = getLocation_xml(city2+"市")
                city_location = "%s %s"%(city2,str(lat_lng))
                city_locationOnlyLatLng = lat_lng
                cityLocationList.append(city_location)
                cityLocationListOnlyLatLng.append(city_locationOnlyLatLng)
                #write2txt(city_location,"City&LocationBS4.txt")
                print city_location
            except:
                print "something wrong about city:",city2
        t +=1

    return cityLocationList,cityLocationListOnlyLatLng
# 返回了一個(gè)有城市和經(jīng)緯度對(duì)應(yīng)的列表如:上海 31.249162,121.487899
# 還返回了一個(gè)只有經(jīng)緯度的列表如 :31.249162,121.487899



#寫入txt操作子函數(shù)
def write2txt(file,txtname):

        f = open(txtname,'a')
        f.write(file)
        f.write("\n")
        f.close()
        # please use this with try except/finall f.close()


# 程序入口
if __name__ == '__main__':

    url = 'http://www.redsh.com/a/20160126/171501.shtml'
    city_locationList,cityLocationListOnlyLatLng= getCityByBS4(url)
    print city_locationList
    for i in city_locationList:
        write2txt(i,"city&locaionByBS4.txt")
    for j in cityLocationListOnlyLatLng:
        write2txt(j,"LocaionOnlyByBS4.txt")

最后效果如圖所示,至于以后要用哪個(gè)直接選擇就行了

有地址對(duì)應(yīng)和只有經(jīng)緯度的

方法2:動(dòng)態(tài)爬取

采用Selenium+PhantomJS的方法(更多Selenium使用案例點(diǎn)擊這里
(這里是有趣的專題),這回沒用Firefox瀏覽器了,也就是演示作用而已,無頭PhantomJS就行了,也是直接上代碼

#采用selenium+phantomjs進(jìn)行動(dòng)態(tài)解析
def getCityBySelenium(url):
    driver = webdriver.PhantomJS(executable_path="phantomjs.exe")
    driver.get(url)
    cityLocationList = []
    cityLocationListOnlyLatLng =[]
    for i in range(6,106):
        elem_city = driver.find_element_by_xpath("http://font[@id='zoom']/p[%d]"%i).text
        elem_city = elem_city.encode('utf-8')
        try:
            city = re.split("[\.]",elem_city)[1]
            city = re.split("(",city)
            city1 = city[0]
            # 一個(gè)中文字符占3個(gè)長度!!
            if len(city1)>13:
                city2 = city1[0:6]
            else:
                city2 =city1.strip()

            lat_lng = getLocation_xml(city2+"市")
            city_location = "%s %s"%(city2,str(lat_lng))
            city_locationOnlyLatLng = lat_lng
            #write2txt(city_location,"City&LocationBySelenium.txt")
            print city_location

            cityLocationList.append(city_location)
            cityLocationListOnlyLatLng.append(city_locationOnlyLatLng)
        except:
            print 'something wrong with ',elem_city
    # 調(diào)用完后記得關(guān)閉!!!!!不然phantomjs一直占用內(nèi)存
    driver.close()
    driver.quit()
    return cityLocationList,cityLocationListOnlyLatLng
    
# 返回了一個(gè)有城市和經(jīng)緯度對(duì)應(yīng)的列表如:上海 31.249162,121.487899
# 還返回了一個(gè)只有經(jīng)緯度的列表如 :31.249162,121.487899

#至于主程序,只要把city_locationList,cityLocationListOnlyLatLng= getCityByBS4(url)中的getCityByBS4(url)換成getCityBySelenium(url),之后改一下txt文件名就行了,當(dāng)然,這些函數(shù)都是在一個(gè)py文件里的

效果就不展示了,和上面靜態(tài)處理一樣


靜態(tài)處理 VS 動(dòng)態(tài)處理

靜態(tài)處理urllib/request
優(yōu)點(diǎn):速度快
缺點(diǎn):處理動(dòng)態(tài)網(wǎng)頁抓取不全

動(dòng)態(tài)處理Selenium+PhantomJS/Firefox
優(yōu)點(diǎn):能夠處理任何靜態(tài)、動(dòng)態(tài)網(wǎng)頁
缺點(diǎn):速度慢

總結(jié):如果能判斷出來是靜態(tài)頁面的,使用urllib和request比較方便和快捷一點(diǎn),但是設(shè)計(jì)交互元素比較多的動(dòng)態(tài)網(wǎng)頁,靜態(tài)方法是沒有辦法處理好的,直接上Selenium把!


正題時(shí)間

獲取城市之后,怎么轉(zhuǎn)化為對(duì)應(yīng)的經(jīng)緯度呢,這里也有兩個(gè)方法

方法1:調(diào)用API接口

簡(jiǎn)單一句話介紹API接口,你不需要知道里面是怎么操作的,只需要知道你輸入什么,經(jīng)過一些亂七八糟的處理后(不用你管),返回給你一個(gè)友好的值就可以了。

舉個(gè)例子,我輸入城市名字,調(diào)用接口后,它返回給我經(jīng)緯度,ok,就是這么簡(jiǎn)單。

這次使用的是百度地圖的API,詳見官網(wǎng)百度地圖API

就像訪問網(wǎng)頁抓取元素一樣,然后她會(huì)返回json/xml格式的數(shù)據(jù),只是,這回的url變成了如下

#獲取json格式的url
http://api.map.baidu.com/geocoder?address=城市&output=json&key=f247cdb592eb43ebac6ccd27f796e2d2
#返回xml格式的url
http://api.map.baidu.com/geocoder?address=城市&output=xml&key=f247cdb592eb43ebac6ccd27f796e2d2

以json返回的舉個(gè)例子,xml一樣道理

#調(diào)用API返回json格式

import urllib2
url= 'http://api.map.baidu.com/geocoder?address=北京&output=json&key=f247cdb592eb43ebac6ccd27f796e2d2'
html = urllib2.urlopen(urllib2.Request(url))
json1 = html.read() #轉(zhuǎn)化為str類型
print json1

返回的形式如下,只需要在爬取其中的lng和lat就行了,至于怎么取出其中的值,可以轉(zhuǎn)化為json字典用字典的方法取值

    "status":"OK",
    "result":{
        "location":{
            "lng":116.395645,
            "lat":39.929986
        },
        "precise":0,
        "confidence":10,
        "level":"\u57ce\u5e02"
    }
}

完整的json格式調(diào)用子函數(shù)

def getLocation_json(addr):
    url= 'http://api.map.baidu.com/geocoder?address=%s&output=json&key=f247cdb592eb43ebac6ccd27f796e2d2'%(addr)
    html = urllib2.urlopen(urllib2.Request(url))
    json1 = html.read() #轉(zhuǎn)化為str類型

    hjson =json.loads(json1) #轉(zhuǎn)化為dict類型
    lng = hjson['result']['location']['lng'] # 經(jīng)度
    lat = hjson['result']['location']['lat'] # 緯度
    lng_lat = [lng, lat]
    return lng_lat

#返回json數(shù)據(jù)格式
{
    "status":"OK",
    "result":{
        "location":{
            "lng":116.395645,
            "lat":39.929986
        },
        "precise":0,
        "confidence":10,
        "level":"\u57ce\u5e02"
    }
}

而采用xml返回形式的如下所示,自己選擇喜歡的返回形式利于你的下一步處理就可以了。

#返回xml數(shù)據(jù)格式
<?xml version="1.0" encoding="utf-8" ?> 
<GeocoderSearchResponse> 
    <status>OK</status>
    <result>
                    <location>
                <lat>39.929986</lat>
                <lng>116.395645</lng>
            </location> 
            <precise>0</precise>
            <confidence>10</confidence>
            <level>城市</level>
            </result>   
</GeocoderSearchResponse>

返回xml格式的完整子程序

def getLocation_xml(addr):
    url= 'http://api.map.baidu.com/geocoder?address=%s&output=xml&key=f247cdb592eb43ebac6ccd27f796e2d2'%(addr)
    html = urllib2.urlopen(urllib2.Request(url))
    xml = html.read()

    bs_getDetail = BeautifulSoup(xml,'lxml')
    #方法一,直接根據(jù)路徑找
    lng =float(bs_getDetail.result.location.lng.string)
    lat = float(bs_getDetail.result.location.lat.string)

    '''
    #方法二,使用find方法+正則表達(dá)式
    lat = bs_getDetail.find('lat')
    lng = bs_getDetail.find('lng')
    pattern = '\d+\.\d+'
    lat = re.findall(pattern,str(lat))[0]
    lng = re.findall(pattern,str(lng))[0]
    '''
    lat_lng = "%s,%s"%(lat,lng)
    return lat_lng



方法2:使用Geopy包

貼心的開發(fā)者已經(jīng)把它放在github上了,(點(diǎn)擊這里進(jìn)行下載)直接下載安裝即可使用。很方便的!

官方用法實(shí)例

官方描述用法

實(shí)現(xiàn)的功能和上面調(diào)用API一樣,輸入城市輸出經(jīng)緯度,而且,還有輸入經(jīng)緯度反向輸出城市的方法,很好用!

以下是我自己用的兩個(gè)子函數(shù)

# 調(diào)用Geopy包進(jìn)行處理-獲取城市名
def getCitynameByGeo(lat_lng):
    #知道經(jīng)緯度獲取地址
    geolocator = Nominatim()
    location = geolocator.reverse(lat_lng)
    addr = location.address
    print addr
    cityname = re.split("[/,]",addr)[-5].strip()
    print cityname

    return addr,cityname

# 調(diào)用Geopy包進(jìn)行處理-獲取經(jīng)緯度
def getLocationByGeo(cityname):
    #知道地址獲取經(jīng)緯度
    geolocator = Nominatim()
    location2 = geolocator.geocode(cityname)
    lat = location2.latitude
    lng = location2.longitude
    return "%s  %s,%s"%(cityname,lat,lng)

直接要用的時(shí)候調(diào)用就可以了,大家可以自己試試,這個(gè)就不用在自己對(duì)json或者xml數(shù)據(jù)進(jìn)行清洗和裁剪了。省時(shí)省力。

測(cè)試一下
就拿剛才用第一種方法寫進(jìn)去的txt中數(shù)據(jù)進(jìn)行測(cè)試

import re
#導(dǎo)入包,實(shí)例化對(duì)象
from geopy.geocoders import Nominatim

# 調(diào)用Geopy包進(jìn)行處理-獲取城市名
def getCitynameByGeo(lat_lng):
    #知道經(jīng)緯度獲取地址
    geolocator = Nominatim()
    location = geolocator.reverse(lat_lng)
    addr = location.address
    print addr
    cityname = re.split("[/,]",addr)[-5].strip()
    return addr,cityname

f = open("LocaionOnlyByBS4.txt")
lines = f.readlines()
for line in lines:
    getCitynameByGeo(line)

測(cè)試效果如下,還是很不錯(cuò)的,就是要提取城市的時(shí)候有點(diǎn)麻煩,格式并不是統(tǒng)一的。

長治路, 虹口區(qū), 虹口區(qū) (Hongkou), 上海市, 200080, 中國
織染局胡同, 東城區(qū), 北京市, 東城區(qū), 北京市, 100010, 中國
春風(fēng)路, 東山街道, 越秀區(qū) (Yuexiu), 廣州市 / Guangzhou, 廣東省, 港方口岸區(qū) Hong Kong Port Area, 510623, 中國

...


調(diào)用API VS Geopy

Geopy比較好吧,畢竟很加符合pythonic,包之間的相互使用,界限分明,而且對(duì)于不熟悉bs4,json,re的童鞋來說,這個(gè)簡(jiǎn)直遇到了親人一樣親切!

當(dāng)然,API的好處也很多,至少我是第一次調(diào)用地圖api,其中包含的內(nèi)容太多了,可以返回的內(nèi)容也不止經(jīng)緯度那么簡(jiǎn)單,如果以后要返回其他的值,估計(jì)還是得靠api


9.18補(bǔ)充

之后的測(cè)試發(fā)現(xiàn),geopy的能力僅限于大城市,如果街道的話,是會(huì)報(bào)錯(cuò)的,而調(diào)用API則照樣能搜索出來,以下是測(cè)試圖

調(diào)用百度地圖API

import urllib2
url= 'http://api.map.baidu.com/geocoder?address=哈爾濱市中央大街112號(hào)&output=json&key=f247cdb592eb43ebac6ccd27f796e2d2'
html = urllib2.urlopen(urllib2.Request(url))
json1 = html.read() 
print json1

結(jié)果返回正常(當(dāng)然精度不能保證,理由大家都懂的)

{
    "status":"OK",
    "result":{
        "location":{
            "lng":126.657717,
            "lat":45.773225
        },
        "precise":0,
        "confidence":10,
        "level":"\u57ce\u5e02"
    }
}

而采用geopy包實(shí)現(xiàn)操作

from geopy.geocoders import Nominatim
def getLocationByGeo(cityname):
    #知道地址獲取經(jīng)緯度
    geolocator = Nominatim()
    location2 = geolocator.geocode(cityname)
    lat = location2.latitude
    lng = location2.longitude
    return "%s  %s,%s"%(cityname,lat,lng)
    
x = getLocationByGeo("哈爾濱市中央大街112號(hào)")
print x

而返回報(bào)錯(cuò)了

AttributeError: 'NoneType' object has no attribute 'latitude'

所以,街道或者比較小的地方還是百度地圖api比較靠譜

btw--大家都要聯(lián)網(wǎng)才能用的0.0


Q&A

1.分割字符串的時(shí)候,竟然出現(xiàn)了?亂碼問題,原來中文字符占三個(gè)長度,,,,好吧,用的挺長時(shí)間的len(),第一次遇到中文,額。

str1 = "中文"
print len(str1)
#6
#一個(gè)中文占了三個(gè)長度!!!!!,難怪出現(xiàn)長度分割?亂碼問題!!!

2.個(gè)別城市調(diào)用API時(shí)出錯(cuò),比如西安這個(gè)城市(使用geopy沒有這個(gè)問題)

lng_lat = getLocation_json("西安")
print lng_lat

#輸出以下
Traceback (most recent call last):
  File "C:/Users/MrLevo/PycharmProjects/test/llll.py", line 208, in <module>
    lng_lat = getLocation_json("西安")
  File "C:/Users/MrLevo/PycharmProjects/test/llll.py", line 203, in getLocation_json
    lng = hjson['result']['location']['lng'] # 經(jīng)度
TypeError: list indices must be integers, not str

于是乎,抖了個(gè)機(jī)靈,加了個(gè)"市",哈哈,解決!百度這API做的也不走心啊,這么大一個(gè)省會(huì)沒有,測(cè)試之后,不僅僅是西安躺槍,100個(gè)城市,就有七八個(gè)讀不出來,,,哎,我都加上了"市",才行。

lng_lat = getLocation_json("西安市")
print lng_lat

#[108.953098, 34.2778]

總結(jié)

以后先找包,再找api,包能實(shí)現(xiàn)的,沒必要那么復(fù)雜。
不過也訓(xùn)練了一下api的調(diào)用,熟悉了一下json,xml,re等。果然時(shí)間久了不用還是會(huì)忘的很快哈~

最后祝大家中秋快樂!然而我還在實(shí)驗(yàn)室加班。。。。苦并快樂著,哈哈


致謝

@MrLevo520--BeautifulSoup使用一兩則(不定期補(bǔ)充)
@MrLevo520--用python做些有趣的事
@轉(zhuǎn)--使用Python解析JSON數(shù)據(jù)的基本方法
@糖拌咸魚--Json概述以及python對(duì)json的相關(guān)操作
@jqnatividad--github geopy
@百度地圖API

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,973評(píng)論 19 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,447評(píng)論 25 708
  • 窯灣古鎮(zhèn)位于新沂市西南邊緣,京杭大運(yùn)河及駱馬湖交匯處。與宿遷、睢寧、邳州三市縣相連,是一座具有千年歷史、聞名全國的...
    阿建w閱讀 552評(píng)論 3 3
  • 我在峰會(huì)上最大的一個(gè)感受就是覺得"不舒服",后來思考了一下原因,發(fā)現(xiàn)最大的問題就是英語和溝通力不行,因?yàn)橛?..
    Liz樂之閱讀 335評(píng)論 0 1
  • 118簧舌動(dòng)張坤/《謀冠江湖》目錄 殺掉郎天之后,燕無憂帶著眾人回到吳縣躲避風(fēng)頭。同時(shí)安排幾個(gè)機(jī)靈的解憂盟兄弟潛伏...
    愚魯公卿閱讀 299評(píng)論 0 9