满打满算也算是编写了5个应用场景的Python脚本,其实做的工作大多是从高德地图那里“偷”数据(对不起,高德)。编写今天这个“GetDistance”的脚本的时候,发觉,其实有很多操作是通用的,比如交互式输入、请求数据、储存数据为Excel表格等等,所以编的过程中整理了一下,把它们都做成自定义函数,方便后期调用。
*如果你对Python一无所知,或者阅读本文时遇到任何不懂得,我建议你后台回复“规划人简单学编程”获取学习笔记
从网络上“偷”数据(真的超级简单)的思路,大体是
确认需求(要怎么偷) → 确定入口(从哪里偷,url [+key]) → 请求网页(开始偷) → 剥离目标数据(挑值钱的) → 保存数据(快,藏起来!) → 结束程序(大声喊出来:偷完了)
(如果网页有防爬虫机制(上锁了,或者像古灵阁那样有复制咒防卫),就另说了)
01 删除上次的保存路径
防止重复写入数据(p是文件路径)
# recreate a file
def file_delete(p):
if os.path.exists(p):
print('检测到"{}"文件夹,为避免重复写入数据将自动删除'.format(p))
try:
shutil.rmtree(p)
except:
input('{}文件夹下可能有打开的文件,请关闭后再运行程序,任意键退出'.format(p))
exit()
if not os.path.exists(p):
os.makedirs(p)
02 确认基础文件
获取哪些数据的“哪些”(p是文件路径)
# file must existence
def exist(p):
if not os.path.exists(p):
input('"{}"文件不存在,请确认,任意键退出'.format(p))
exit()
03 读取本地的Excel表格
确认存在后,就开始读取了(p文件路径, c表格列数确认(最简单的确认,不过还是不敢保证数据是合乎要求的,也就是说还是存在程序执行中遇错自动退出的情况), f用于储存读取内容的list)
# open a xlsx file and read data
def excel_read(p, c, f):
exist(p)
data = xlrd.open_workbook(p)
table = data.sheets()[0]
lines = table.nrows
cols = table.ncols
if cols != c:
input('"{}"文件格式不正确,请确认,任意键退出'.format(p))
exit()
for i in range(lines - 1):
f.append(table.row_values(i + 1))
return
04 检查key的有效性
开放API类数据的获取(p是文件路径)
# check key
def key_check(p):
print('正在读取"{}"文件(高德web服务端API接口)'.format(p))
exist(p)
k= open(p, 'r', encoding='utf-8')
key = k.read()
if len(key) == 0:
input('key.txt为空')
exit()
#这一步是为了检查key是否有效,region_find是另一个自定义函数,后面需要替换
info = region_find(118.779425, 32.055004, key)[1]
if info != 'OK':
input('key出错,原因:{}'.format(info))
exit()
print('Key读取成功,确认有效!')
return key
05 获取坐标点的行政区名称
高德的API(lgt经度, lat纬度, k密匙)
# define administrative region
def region_find(lgt, lat, k):
url ='https://restapi.amap.com/v3/geocode/regeo?location={},{}&key={}'.format(lgt,lat, k)
# get_data是另一个自定义函数,用于打开url,返回数据(info是url是否获取成功的提示)
data, info = get_data(url, 'region')
region = ''
if info.upper() == 'OK':
rg = data['regeocode']['addressComponent']
# municipality has no city part and county has no district part
region = (str(rg['province']) + str(rg['city']) +str(rg['district'])).replace('[]', '')
else:
print('行政区域识别失败,原因:{}'.format(info))
return region, info
06 打开url获取数据
数据获取的第一步
# request data from internet
def get_data(u, m):
#avoid key limitation
time.sleep(1)
data =json.loads(request.urlopen(u).read())
# 这里是基于高德的开发文档定制的部分
if m == 'bicycling':
m = 'errmsg'
else:
m = 'info'
return data, data[m]
07 交互式输入的检查
有时候我们需要根据程序使用者的输入,定制个性化的数据获取方案(must_in输入的必须是哪些, l最大输入长度, f用于储存有效输入的list-需要在主程序中定义)
# interaction check
def input_check(must_in, l, f):
#three chances
for i in range(4):
if i == 3:
input('请重新运行脚本,任意键退出')
exit()
r = input()
# numbers limit
if len(r) == 0:
print('输入值为空,剩余输入次数为:', (2 - i))
continue
elif len(r) > l:
print('输入值超出限制,剩余输入次数为:', (2 - i))
continue
else:
rr = r.split(',')
# avoid same number
g = 'out'
rrr = set()
for j in rr:
# must fit the set mode
for k in must_in:
if int(j) == k:
g = 'in'
rrr.add(j)
break
else:
g = 'out'
if g == 'out':
print('输入错误,剩余输入次数为:', (2 - i))
continue
else:
for m in rrr:
f.append(m)
break
08 合并两个列表的数据
我习惯于把剥离的目标数据先储存在数组中,如果有多个目标数据组,那么可能是这样的,组1:[(1, 1), (2, 2],组2:[(1, 1), (2, 2)]……为方便最终的数组写入Excel,还是要把数据组的数据合并在一个里面,组:[(1, 1,1, 1, ……), (2, 2, 2, 2, ……), ……]
仅支持两两合并,多于两组的可以重复多次操作(ls1组1, ls2组2, ls3合并组需在主程序先定义)
# combine data from two lists which hassame length but contains tuple and the like
def list_combine(ls1, ls2, ls3):
for k in range(len(ls1)):
ex = []
for i in ls1[k]:
ex.append(i)
for j in ls2[k]:
ex.append(j)
ls3.append(ex)
09 将数据写入Excel
(ph文件保存路径, data需要写入的数据所在的list)
# create a table and write in data
def excel_write(ph, data):
table = xlsxwriter.Workbook(ph)
sheet = table.add_worksheet('sheet1')
#write head
sheet.write(0, 0, 'ID')
for k in range(len(data[0])):
sheet.write(0, k + 1, data[0][k])
#write data
for i in range(len(data) - 1):
# write ID column
sheet.write(i + 1, 0, i + 1)
for j in range(len(data[i + 1])):
try:
sheet.write(i + 1, j + 1,data[i + 1][j])
except:
sheet.write(i + 1, j + 1,str(data[i + 1][j]))
table.close()
print('###save as ' + ph)
10 结束程序的提示
挺清晰、美丽的提示
# end remark
def end():
print('\n'
'\n****************************'
'\n*数据已全部获取,任意键退出*'
'\n****************************\n\n')
input()
exit()
11 坐标转换脚本
转换总是不太准确的,但是都大体能用(代码来自网络,涵盖百度坐标系、GCJ-02坐标系、WGS-84坐标系;传入的参数为经度,纬度)
def bd09togcj02(bd_lon, bd_lat):
x_pi = 3.14159265358979324 * 3000.0 / 180.0
x= bd_lon - 0.0065
y= bd_lat - 0.006
z= math.sqrt(x * x + y * y) - 0.00002 * math.sin(y * x_pi)
theta = math.atan2(y, x) - 0.000003 * math.cos(x * x_pi)
gg_lng = z * math.cos(theta)
gg_lat = z * math.sin(theta)
#minus based on test
return [gg_lng-0.012, gg_lat-0.012]
def gcj02tobd09(lng, lat):
x_PI = 3.14159265358979324 * 3000.0 / 180.0
z= math.sqrt(lng * lng + lat * lat) + 0.00002 * math.sin(lat * x_PI)
theta = math.atan2(lat, lng) + 0.000003 * math.cos(lng * x_PI)
bd_lng = z * math.cos(theta) + 0.0065
bd_lat = z * math.sin(theta) + 0.006
#minus based on test
return [bd_lng-0.006264, bd_lat-0.001279]
def wgs84togcj02(lng, lat):
PI = 3.1415926535897932384626
ee = 0.00669342162296594323
a= 6378245.0
dlat = transformlat(lng - 105.0, lat - 35.0)
dlng = transformlng(lng - 105.0, lat - 35.0)
radlat = lat / 180.0 * PI
magic = math.sin(radlat)
magic = 1 - ee * magic * magic
sqrtmagic = math.sqrt(magic)
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI)
dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * PI)
mglat = lat + dlat
mglng = lng + dlng
return [mglng, mglat]
def gcj02towgs84(lng,lat):
PI = 3.1415926535897932384626
ee = 0.00669342162296594323
a= 6378245.0
dlat = transformlat(lng - 105.0, lat - 35.0)
dlng = transformlng(lng - 105.0, lat - 35.0)
radlat = lat / 180.0 * PI
magic = math.sin(radlat)
magic = 1 - ee * magic * magic
sqrtmagic = math.sqrt(magic)
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI)
dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * PI)
mglat = lat + dlat
mglng = lng + dlng
return [str(lng * 2 - mglng), str(lat * 2 - mglat)]
def transformlat(lng, lat):
PI = 3.1415926535897932384626
ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * \
lat + 0.1 * lng * lat + 0.2 * math.sqrt(abs(lng))
ret += (20.0 * math.sin(6.0 * lng * PI) + 20.0 *
math.sin(2.0 * lng * PI)) * 2.0 / 3.0
ret += (20.0 * math.sin(lat * PI) + 40.0 *
math.sin(lat / 3.0 * PI)) * 2.0 / 3.0
ret += (160.0 * math.sin(lat / 12.0 * PI) + 320 *
math.sin(lat * PI / 30.0)) * 2.0 / 3.0
return ret
def transformlng(lng, lat):
PI = 3.1415926535897932384626
ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + \
0.1 * lng * lat + 0.1 * math.sqrt(abs(lng))
ret += (20.0 * math.sin(6.0 * lng * PI) + 20.0 *
math.sin(2.0 * lng * PI)) * 2.0 / 3.0
ret += (20.0 * math.sin(lng * PI) + 40.0 *
math.sin(lng / 3.0 * PI)) *2.0 / 3.0
ret += (150.0 * math.sin(lng / 12.0 * PI) + 300.0 *
math.sin(lng / 30.0 * PI)) * 2.0 / 3.0
return ret
def bd09towgs84(lng, lat):
lng1, lat1 = bd09togcj02(lng, lat)
return gcj02towgs84(lng1, lat1)
def wgs84tobd09(lng, lat):
lng1, lat1 = wgs84togcj02(lng, lat)
return gcj02tobd09(lng1, lat1)
就是这些了吧~