经纬度地址转换的方法集合(Python描述)

Python 2.7
IDE Pycharm 5.0.3
Geopy 1.11


前言

这只是我想做的一部分,写一块太大了,单独记录

目的

获取2015年GDP TOP100城市并获取城市对应经纬度,存入txt后续操作

获取GDP TOP100城市

查询网址如下http://www.redsh.com/a/20160126/171501.shtml
2015中国100大城市GDP排行榜TOP100出炉!

页面的效果大概是这样的~惨不忍睹的格式,花了我好多时间提取。。。。

元素大概是这样的

ok,先把TOP100城市爬下来再说


方法1:静态爬取

采用urllib2+BS4+re(更多静态爬取方法点击这里),当然你也可以采用request来代替urllib2,这里我只做个试验,所以用了urllib2,废话不多说,直接上代码!

# 采用urllib2+BS4进行静态网页解析
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')
            # 数据清洗过程
            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
# 返回了一个有城市和经纬度对应的列表如:上海 31.249162,121.487899
# 还返回了一个只有经纬度的列表如 :31.249162,121.487899



#写入txt操作子函数
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")

最后效果如图所示,至于以后要用哪个直接选择就行了

有地址对应和只有经纬度的

方法2:动态爬取

采用Selenium+PhantomJS的方法(更多Selenium使用案例点击这里
(这里是有趣的专题),这回没用Firefox浏览器了,也就是演示作用而已,无头PhantomJS就行了,也是直接上代码

#采用selenium+phantomjs进行动态解析
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("//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]
            # 一个中文字符占3个长度!!
            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
    # 调用完后记得关闭!!!!!不然phantomjs一直占用内存
    driver.close()
    driver.quit()
    return cityLocationList,cityLocationListOnlyLatLng
    
# 返回了一个有城市和经纬度对应的列表如:上海 31.249162,121.487899
# 还返回了一个只有经纬度的列表如 :31.249162,121.487899

#至于主程序,只要把city_locationList,cityLocationListOnlyLatLng= getCityByBS4(url)中的getCityByBS4(url)换成getCityBySelenium(url),之后改一下txt文件名就行了,当然,这些函数都是在一个py文件里的

效果就不展示了,和上面静态处理一样


静态处理 VS 动态处理

静态处理urllib/request
优点:速度快
缺点:处理动态网页抓取不全

动态处理Selenium+PhantomJS/Firefox
优点:能够处理任何静态、动态网页
缺点:速度慢

总结:如果能判断出来是静态页面的,使用urllib和request比较方便和快捷一点,但是设计交互元素比较多的动态网页,静态方法是没有办法处理好的,直接上Selenium把!


正题时间

获取城市之后,怎么转化为对应的经纬度呢,这里也有两个方法

方法1:调用API接口

简单一句话介绍API接口,你不需要知道里面是怎么操作的,只需要知道你输入什么,经过一些乱七八糟的处理后(不用你管),返回给你一个友好的值就可以了。

举个例子,我输入城市名字,调用接口后,它返回给我经纬度,ok,就是这么简单。

这次使用的是百度地图的API,详见官网百度地图API

就像访问网页抓取元素一样,然后她会返回json/xml格式的数据,只是,这回的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返回的举个例子,xml一样道理

#调用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() #转化为str类型
print json1

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

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

完整的json格式调用子函数

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() #转化为str类型

    hjson =json.loads(json1) #转化为dict类型
    lng = hjson['result']['location']['lng'] # 经度
    lat = hjson['result']['location']['lat'] # 纬度
    lng_lat = [lng, lat]
    return lng_lat

#返回json数据格式
{
    "status":"OK",
    "result":{
        "location":{
            "lng":116.395645,
            "lat":39.929986
        },
        "precise":0,
        "confidence":10,
        "level":"\u57ce\u5e02"
    }
}

而采用xml返回形式的如下所示,自己选择喜欢的返回形式利于你的下一步处理就可以了。

#返回xml数据格式
<?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')
    #方法一,直接根据路径找
    lng =float(bs_getDetail.result.location.lng.string)
    lat = float(bs_getDetail.result.location.lat.string)

    '''
    #方法二,使用find方法+正则表达式
    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包

贴心的开发者已经把它放在github上了,(点击这里进行下载)直接下载安装即可使用。很方便的!

官方用法实例

官方描述用法

实现的功能和上面调用API一样,输入城市输出经纬度,而且,还有输入经纬度反向输出城市的方法,很好用!

以下是我自己用的两个子函数

# 调用Geopy包进行处理-获取城市名
def getCitynameByGeo(lat_lng):
    #知道经纬度获取地址
    geolocator = Nominatim()
    location = geolocator.reverse(lat_lng)
    addr = location.address
    print addr
    cityname = re.split("[/,]",addr)[-5].strip()
    print cityname

    return addr,cityname

# 调用Geopy包进行处理-获取经纬度
def getLocationByGeo(cityname):
    #知道地址获取经纬度
    geolocator = Nominatim()
    location2 = geolocator.geocode(cityname)
    lat = location2.latitude
    lng = location2.longitude
    return "%s  %s,%s"%(cityname,lat,lng)

直接要用的时候调用就可以了,大家可以自己试试,这个就不用在自己对json或者xml数据进行清洗和裁剪了。省时省力。

测试一下
就拿刚才用第一种方法写进去的txt中数据进行测试

import re
#导入包,实例化对象
from geopy.geocoders import Nominatim

# 调用Geopy包进行处理-获取城市名
def getCitynameByGeo(lat_lng):
    #知道经纬度获取地址
    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)

测试效果如下,还是很不错的,就是要提取城市的时候有点麻烦,格式并不是统一的。

长治路, 虹口区, 虹口区 (Hongkou), 上海市, 200080, 中国
织染局胡同, 东城区, 北京市, 东城区, 北京市, 100010, 中国
春风路, 东山街道, 越秀区 (Yuexiu), 广州市 / Guangzhou, 广东省, 港方口岸區 Hong Kong Port Area, 510623, 中国

...


调用API VS Geopy

Geopy比较好吧,毕竟很加符合pythonic,包之间的相互使用,界限分明,而且对于不熟悉bs4,json,re的童鞋来说,这个简直遇到了亲人一样亲切!

当然,API的好处也很多,至少我是第一次调用地图api,其中包含的内容太多了,可以返回的内容也不止经纬度那么简单,如果以后要返回其他的值,估计还是得靠api


9.18补充

之后的测试发现,geopy的能力仅限于大城市,如果街道的话,是会报错的,而调用API则照样能搜索出来,以下是测试图

调用百度地图API

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

结果返回正常(当然精度不能保证,理由大家都懂的)

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

而采用geopy包实现操作

from geopy.geocoders import Nominatim
def getLocationByGeo(cityname):
    #知道地址获取经纬度
    geolocator = Nominatim()
    location2 = geolocator.geocode(cityname)
    lat = location2.latitude
    lng = location2.longitude
    return "%s  %s,%s"%(cityname,lat,lng)
    
x = getLocationByGeo("哈尔滨市中央大街112号")
print x

而返回报错了

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

所以,街道或者比较小的地方还是百度地图api比较靠谱

btw--大家都要联网才能用的0.0


Q&A

1.分割字符串的时候,竟然出现了�乱码问题,原来中文字符占三个长度,,,,好吧,用的挺长时间的len(),第一次遇到中文,额。

str1 = "中文"
print len(str1)
#6
#一个中文占了三个长度!!!!!,难怪出现长度分割�乱码问题!!!

2.个别城市调用API时出错,比如西安这个城市(使用geopy没有这个问题)

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'] # 经度
TypeError: list indices must be integers, not str

于是乎,抖了个机灵,加了个"市",哈哈,解决!百度这API做的也不走心啊,这么大一个省会没有,测试之后,不仅仅是西安躺枪,100个城市,就有七八个读不出来,,,哎,我都加上了"市",才行。

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

#[108.953098, 34.2778]

总结

以后先找包,再找api,包能实现的,没必要那么复杂。
不过也训练了一下api的调用,熟悉了一下json,xml,re等。果然时间久了不用还是会忘的很快哈~

最后祝大家中秋快乐!然而我还在实验室加班。。。。苦并快乐着,哈哈


致谢

@MrLevo520--BeautifulSoup使用一两则(不定期补充)
@MrLevo520--用python做些有趣的事
@转--使用Python解析JSON数据的基本方法
@糖拌咸鱼--Json概述以及python对json的相关操作
@jqnatividad--github geopy
@百度地图API

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,392评论 25 707
  • 窑湾古镇位于新沂市西南边缘,京杭大运河及骆马湖交汇处。与宿迁、睢宁、邳州三市县相连,是一座具有千年历史、闻名全国的...
    阿建w阅读 517评论 3 3
  • 我在峰会上最大的一个感受就是觉得"不舒服",后来思考了一下原因,发现最大的问题就是英语和沟通力不行,因为英...
    Liz乐之阅读 303评论 0 1
  • 118簧舌动张坤/《谋冠江湖》目录 杀掉郎天之后,燕无忧带着众人回到吴县躲避风头。同时安排几个机灵的解忧盟兄弟潜伏...
    愚鲁公卿阅读 281评论 0 9