用 GoogleMaps 的 API 生成经纬度 - 城市名表

今天和同事聊天,突然提到怎么通过经纬度获取城市信息。找了一下网上似乎没有现成的数据库,于是用了半小时撸了个暴力程序。

原理就是:

  1. 弄个线程池
  2. 暴力调用 GoogleAPI 去获取位置信息
  3. 记在 redis 里面

本文仅为娱乐,因为有现成的数据库。请看末尾链接

Google 的 API 出来可能很多都是英文,但是我只想要中文的信息怎么办?

可以弄个函数做判断:

1
2
3
4
5
def check_contain_chinese(check_str):
for ch in check_str:
if u'\u4e00' <= ch <= u'\u9fff':
return True
return False

直接贴全部的代码,写得很挫,抛砖引玉吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/usr/bin/python
# -*- coding: UTF-8 -*-

import thread, json, urllib, redis, threadpool, sys

db = redis.StrictRedis(host='localhost', port=6379, db=0)

def check_contain_chinese(check_str):
for ch in check_str:
if u'\u4e00' <= ch <= u'\u9fff':
return True
return False

def req(data):
cur_data_idx, lat, lng = data[0], data[1], data[2]
url = "https://maps.googleapis.com/maps/api/geocode/json?latlng=%s,%s&sensor=true&language=zh-CN&key=这儿请替换成你自己的GoogleAPI的Key"%(lat, lng)
r = urllib.urlopen(url)
txt = r.read()
if not "ZERO_RESULTS" in txt:
data = json.loads(txt)
for c in data['results']:
for d in c["address_components"]:
if "locality" in d['types'] or "administrative_area_level_1" in d['types'] or 'political' in d['types']:
name = d["long_name"]
if check_contain_chinese(name):
db.set("%s-%s"%(lat,lng), name)
cur_data_idx = cur_data_idx + 1
print "Idx:%s Lat:%s Lng:%s CityName:%s"%(cur_data_idx, lat, lng, name.encode('utf-8'))
return

max_lat = 53
min_lat = 23
min_lng = 113
max_lng = 135

def check():
pass

if __name__ == '__main__':
lat_range = range(min_lat*10, max_lng*10)
lng_range = range(min_lng*10, max_lng*10)
l = []
idx = 0
for lat in lat_range:
for lng in lng_range:
idx = idx + 1
l.append([idx, lat/10.0, lng/10.0])
pool = threadpool.ThreadPool(100)
reqs = threadpool.makeRequests(req, l)
[pool.putRequest(req) for req in reqs]
pool.wait()


随便一扫就是几千条数据了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Idx:5104 Lat:25.3 Lng:117.2 CityName:新罗区
Idx:5068 Lat:25.3 Lng:113.6 CityName:仁化县
Idx:5086 Lat:25.3 Lng:115.4 CityName:安远县
Idx:4886 Lat:25.2 Lng:117.4 CityName:漳平市
Idx:5062 Lat:25.3 Lng:113.0 CityName:乐昌市
Idx:5092 Lat:25.3 Lng:116.0 CityName:会昌县
Idx:5078 Lat:25.3 Lng:114.6 CityName:南雄市
Idx:5119 Lat:25.3 Lng:118.7 CityName:仙游县
Idx:5120 Lat:25.3 Lng:118.8 CityName:仙游县
Idx:5147 Lat:25.3 Lng:121.5 CityName:新庄里
Idx:5067 Lat:25.3 Lng:113.5 CityName:乐昌市
Idx:5117 Lat:25.3 Lng:118.5 CityName:永春县
Idx:5105 Lat:25.3 Lng:117.3 CityName:新罗区
Idx:5144 Lat:25.3 Lng:121.2 CityName:台湾
Idx:5110 Lat:25.3 Lng:117.8 CityName:安溪县
Idx:5130 Lat:25.3 Lng:119.8 CityName:中国
Idx:5122 Lat:25.3 Lng:119.0 CityName:城厢区
Idx:5085 Lat:25.3 Lng:115.3 CityName:安远县
Idx:5093 Lat:25.3 Lng:116.1 CityName:武平县

还有些问题

  1. 有些坐标可能并未有数据,可能需要做二次处理,寻找最近的坐标。
  2. 精度不够,内存大。为了图方便直接暴力 range 了。xrange 都没用上。想真正的扫遍全世界,还是按需生成坐标点吧。
  3. API 可以通过城市名反查经纬度信息。比如 https://maps.googleapis.com/maps/api/geocode/json?address=%E9%87%8D%E5%BA%86 返回的内容中就有
1
2
3
4
5
6
7
8
9
10
"bounds" : {
"northeast" : {
"lat" : 32.2011871,
"lng" : 110.1998582
},
"southwest" : {
"lat" : 28.1602253,
"lng" : 105.2897606
}
},

但是根据文档,这是个 optionally returned。所以,通过暴力扫描 + bounds 查询,应该能省很多很多时间,并且也能满足精确需求。

当然,最好的就是有个数据库。其实,maxmind 已经做了这个事情了,所以本文只是娱乐而已。:D

作者

薯条

发布于

2015-10-15

更新于

2024-01-15

许可协议

评论