起因
最近做规划项目,领导让查出某几个市的所有乡镇级行政区域,距离所在县级行政中心的交通运输距离。想着也不是啥难事儿,高德地图一搜就有。
当我把各市行政区划统计完,发现一共有五百多个乡镇,意味着要在地图上搜索五百多次,立马脑仁疼。
于是我把任务分工给了其他同事,几个人同时查,能快点。
看看,思维就是这么僵化,没有立刻意识到重复劳动也许可以借助计算机来自动完成。
边查数据边想,以后还会有此类任务,到时候没同事帮忙可咋办,虽然不费脑力,却消耗大量时间。忽然意识到,我会基本的 Python 啊,搜搜网上有没有自动查距离的教程。
果然搜到一篇文章,《Python 批量获取高德地图地理信息数据》。然后参考博主的代码,写了一个自动从高德地图获取地理经纬度和运输距离的 Python 脚本,配合 xlxs 格式文件使用,可批量查找数据并存储进 xlxs 格式文件。
准备工作
1. 获取高德开放平台的应用密钥
在 高德开放平台 上注册一个帐号,然后创建一个 web 服务类型的应用,再给该应用添加一个密钥 key,记住这个 key。
2. 处理 workbook.xlsx 文件
创建一个 xlsx 格式文件,命名为 workbook。将需要查询的地名放进 workbook.xlsx 的 Sheet1 工作表中,Python 脚本自动获取的经纬度和运输距离将写入 Sheet2 工作表中。因此该文件中需已存在 Sheet1 和 Sheet2 两个工作表。
Sheet1 中按下图格式填写地名。每个县级行政区域内的地名放置一列,第一行为该县级行政区域名称,第二行及之后为其下乡镇级行政区域名称。写入的地名必须全部属于一个地级市。如果要获取多个市的数据,只能先写入一个市,运行完脚本后,再对另一个市重复操作。
image
3. 安装 Python 环境和 openpyxl 库
安装 Python 环境不表。
利用 Python 的 openpyxl 库从 xlsx 格式文件中读写数据,因此先安装此库。
pip install openpyxl
4. 创建 Python 脚本
新建一个 py 脚本文件,名称随意,如 workscript.py,用文本编辑器打开,将以下代码复制进去保存。该脚本文件应与 workbook.xlsx 在同一目录中。
# 对 Sheet1 中多列数据求值,并将结果输出为 Sheet2 中一列
import urllib.request # 发送请求
from urllib import parse # URL 编码
import json # 解析 json 数据
from openpyxl import load_workbook # 从 Excel 中读取乡镇名称
def city_location(localname, somewhere, thekey):
url1 = (
'http://restapi.amap.com/v3/place/text?keywords=' + localname +
'&city=' + somewhere +
'&citylimit=true&output=json&offset=1&page=1&key=' + thekey
)
# 将一些符号进行 URL 编码
newUrl1 = parse.quote(url1, safe="/:=&?#+!$,;'@()*[]")
# 发送请求
response1 = urllib.request.urlopen(newUrl1)
# 读取数据
data1 = response1.read()
# 解析 json 数据
jsonData1 = json.loads(data1)
# 从 json 文件中提取座标
try:
a = jsonData1['pois'][0]['location']
except:
return '查询不到坐标'
else:
return a
def road_long(origin, destination, thekey):
url2 = (
'https://restapi.amap.com/v3/direction/driving?origin=' + origin +
'&destination=' + destination +
'&extensions=all&strategy=1&output=json&key=' + thekey
)
# 将一些符号进行 URL 编码
newUrl2 = parse.quote(url2, safe="/:=&?#+!$,;'@()*[]")
# 发送请求
response2 = urllib.request.urlopen(newUrl2)
# 接收数据
data2 = response2.read()
# 解析 json 文件
jsonData2 = json.loads(data2)
# 从 json 文件中提取距离
try:
a = jsonData2['route']['paths'][0]['distance']
except:
return '查询不到距离'
else:
return a
somewhere = input('地点所在地级市名称:')
thekey = input('高德开放平台密钥:')
book = load_workbook('workbook.xlsx')
nameSheet = book["Sheet1"]
locationsheet = book['Sheet2']
tol = 0
for index1 in range(ord('A'), ord('A')+nameSheet.max_column):
index1 = chr(index1)
nameList = []
for row in range(1, nameSheet.max_row+1):
nameList.append(nameSheet[index1+str(row)].value)
nameList = [x for x in nameList if x is not None] # 删除空字符
print(nameList)
dict1 = {} # 创建一个字典用于接收坐标数据
dict2 = {} # 创建一个字典用于接收距离数据
for cityname in nameList:
dict1[cityname] = city_location(cityname, somewhere, thekey)
for cityname in nameList[1::]:
if dict1[cityname] == '查询不到坐标':
dict2[cityname] = '此地查询不到坐标值'
else:
dict2[cityname] = road_long(
dict1[cityname], dict1[nameSheet[index1+'1'].value], thekey
)
dict2[nameSheet[index1+'1'].value] = '行政中心'
print(dict1)
print(dict2)
list1 = []
for cityname in nameList:
list2 = [cityname, dict1[cityname], dict2[cityname]]
list1.append(list2)
print(list1)
for row in range(len(nameList)):
(
locationsheet['A%d' % (row+1+tol)],
locationsheet['B%d' % (row+1+tol)],
locationsheet['C%d' % (row+1+tol)]
) = list1[row]
tol += len(nameList)
book.save('workbook.xlsx')
运行脚本获取数据
使用终端在脚本目录下运行该脚本。会提示输入「地点所在地级市名称」和「高德开放平台密钥」。为避免全国范围内地名重复导致查询距离偏差,脚本将查询范围约束到乡镇所在的地级市,因此需要输入「地点所在地级市名称」。「高德开放平台密钥」则为之前创建的高德平台 web 服务应用 key。
输入完毕后,脚本开始从 workbook.xlsx 文件中读取地名,然后自动发送至高德地图查找经纬度和运输距离,再将数据保存到 workbook.xlsx 文件中。正常运行下,终端中也会显示获取到的数据。
待脚本运行完,打开 workbook.xlsx,所需要查找的运输距离就在工作表 Sheet2 中。如下图所示,第一列为地名,第二列为经纬度,第三列为该乡镇到所在县级行政中心的运输距离。如果是县名,第三列则标注「行政中心」。
image
说明
高德地图查询两个地点之间的运输距离,需要提交两个地点的经纬度,因此脚本先从高德地图获取各个地点的经纬度,顺便保存在 Sheet2 中。
高德地图查询两个地点之间的运输距离,驾车路径规划有「距离有线」、「速度有线」、「费用优先」等策略,该脚本选择的是「费用优先」,即不走收费路段,且耗时最少的路线。
所查询距离单位为米。
一个高德开放平台 key,一天只能查询 2000 次经纬度,2000 次驾驶距离。所以如果查询地点超过 2000 个,要么多创建几个 key,要么等第二天再查。
欢迎关注微信公众号:[桃源乡吃桃](kktaoyuanxiang)