也不前情提要了,本博客内容高度概括就是:通过直接遍历法和依据文本法获取一个城市的“所有”线路名,并计算这些线路的平均路线长度、平均站点数、平均站距(“直线系数”将在下一篇博客中讨论)
(1)程序任务:
通过高德地图PAI,爬取一个城市所有(可选)公交基本信息,并保存到表格中。采用循环遍历一定范围的数字实现,不封装函数(好处是,方便查看变量信息,及时发现错误,利于修改)
(2)可直接出结果的代码
遍历青岛前10路公交基本信息
import requests
import json
import pandas as pd
import time
if __name__=="__main__":
t0=time.time()#用于记录开始时间
bus_num=0 #计算有效遍历的公交数
city='青岛' #需要查询公交信息的城市
for_num=10 #遍历的线路数[1路,for_num路],通常公交线路数小于1000,具体可参考8684等网站
all_bus=pd.DataFrame()
for i in range(1,for_num+1):
line=str(i)+'路'
try:
#1、获取数据
url = 'https://restapi.amap.com/v3/bus/linename?s=rsv3&extensions=all&key=a5b7479db5b24fd68cedcf24f482c156&output=json&city={}&offset=1&keywords={}&platform=JS'.format(city,line)
r = requests.get(url).text
rt = json.loads(r)
#2、读取当前公交线路主要信息
dt = {}
dt['line_name'] = rt['buslines'][0]['name'] #公交线路名字
dt['bounds'] = rt['buslines'][0]['bounds'] #行车区间(非始发站,终点站坐标)
dt['distance'] = rt['buslines'][0]['distance'] #全程长度
dt['station'] = int(rt['buslines'][0]['busstops'][-1]['sequence']) #全程站点数
bus_num+=1 #有效公交数+1
df2=pd.DataFrame(dt,index=[bus_num]) #返回pd.DataFrame()类型
all_bus=pd.concat([all_bus,df2]) #不加这个'路'可能优先获取地铁
except:
print('没有{}公交'.format(line)) #正常情况下,这条语句不会执行
print("Bus_info函数遍历{}前{}路公交,有效公交线路数为:{}个".format(city,for_num,bus_num))
all_bus.to_csv("{}前{}路公交(有效线路数:{})基本信息.csv".format(city,for_num,bus_num),encoding='utf-8-sig')
t1=time.time()#用于记录结束时间
print("用时:%.2fs"%(t1-t0))
(3)程序结果:
(4)小结
大部分内容在之前的部分都介绍过了,这里获取站点数的代码值得好好品味一下(可以通过循环得到,但这样显得有些多余),啥也别说了,“数据就是程序员的生命”
dt['station'] = int(rt['buslines'][0]['busstops'][-1]['sequence'])
(1)程序内容介绍:
循环青岛市的1000路以内的公交(有的线路没有,且有的线路包含),获取这些线路的:路线名、行车区间、路程长度、站点数、区间直线距离等数据,并计算出这些公交线路的平均长度、平均站点数、平均站距,最后在python同目录下保存为表格。
(2)代码
小说明:我本意是希望大家拿我的代码就能出结果的,且不希望代码出现大的错误,但毕竟写的内容多了,改的地方也多,代码习惯也在培养的初期,加上生活中琐事也多,肯定是没精力做校对的,emmmmm,就这样
import requests
import json
import pandas as pd
import time
from math import sin, asin, cos, radians, fabs, sqrt
#自己写的用于记录时间函数
def record_time(flag):
if flag==0:
global t0
t0=time.time()
else:
t1=time.time()
print("用时:%.2fs"%(t1-t0))
print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))
#python计算两点间直线距离
def Geodistance(lng1,lat1,lng2,lat2):
lng1, lat1, lng2, lat2 = map(radians, [lng1, lat1, lng2, lat2])
dlon=lng2-lng1
dlat=lat2-lat1
a=sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
dis=2*asin(sqrt(a))*6371*1000
return dis
#或许公交信息:线路名、始发站、终点站、行车区间(坐标)、路程、行车区间直线距离
def Bus_inf(city,line):
global bus_num #全局变量,用于计算公交数目
try:
#获取数据
url = 'https://restapi.amap.com/v3/bus/linename?s=rsv3&extensions=all&key=a5b7479db5b24fd68cedcf24f482c156&output=json&city={}&offset=1&keywords={}&platform=JS'.format(city,line)
r = requests.get(url).text
rt = json.loads(r)
#读取当前公交线路主要信息
dt = {}
dt['line_name'] = rt['buslines'][0]['name'] #公交线路名字
dt['start_stop'] = rt['buslines'][0]['start_stop'] #始发站
dt['end_stop'] = rt['buslines'][0]['end_stop'] #终点站
dt['bounds'] = rt['buslines'][0]['bounds'] #行车区间(非始发站,终点站坐标)
dt['distance'] = rt['buslines'][0]['distance'] #全程长度
dt['station'] = int(rt['buslines'][0]['busstops'][-1]['sequence']) #全程站点数(包括始发站和终点站)
lng1,lat1 =rt['buslines'][0]['bounds'].split(';')[0].split(',')
lng2,lat2 =rt['buslines'][0]['bounds'].split(';')[1].split(',')
dt['straight_dis'] = Geodistance(float(lng1),float(lat1),float(lng2),float(lat2)) #计算行车区间bounds的直线距离
bus_num+=1 #有效公交数+1
return pd.DataFrame(dt,index=[bus_num]) #下标index为“第几条公交线”
except:
print('没有{}公交'.format(line)) #正常情况下,这条语句不会执行
return pd.DataFrame() #读取数据失败,返回空的
def Bus_analysis(bus_info):
#路程数据需要转float类型
print('公交线路的平均长度:{:.6f} km'.format(bus_info["distance"].astype(float).mean()))
print('公交线路的平均站点数:{:.6f} 个'.format(bus_info['station'].mean()))
#错误站距求解方法:先求出每条线路的平均站距,相加,再除总线路数,得到的总的平均站距是不合适的!(涉及到加权问题)
#站距求解方法1(不妥):平均长度/平均站点数 (这里的站点数包括了起始站和终点站,求平均站距时每条线路应该去掉一个站!)
print('公交线路的平均站距(求法1)为:{:.6f} km/站'.format(bus_info["distance"].astype(float).mean()/bus_info['station'].mean()))
#求站距方法2(正确):路线总长/(求出所有站点数总和-线路数)
print('公交线路的平均站距(求法2)为:{:.6f} km/站'.format(bus_info["distance"].astype(float).sum()/(bus_info['station'].sum()-bus_num)))
if __name__=="__main__":
record_time(0)
bus_num=0 #设置全局变量数值(通常默认就是0)
city='青岛' #需要查询公交信息的城市
for_num=10 #遍历的线路数[1路,for_num路],通常公交线路数小于1000,具体可参考8684等网站
all_buslines=pd.DataFrame()
for i in range(1,for_num+1):
all_buslines=pd.concat([all_buslines,Bus_inf(city,str(i)+'路')]) #不加这个'路'可能优先获取地铁
print("Bus_info函数遍历{}前{}路公交,有效公交线路数为:{}个的情况下:".format(city,for_num,bus_num))
Bus_analysis(all_buslines)
#all_buslines.to_csv("{}前{}路公交(有效线路数:{})基本信息.csv".format(city,for_num,bus_num),encoding='utf-8-sig')
record_time(1)
③如果只想知道市区线路呢?
(1)说明
通过for循环获取公交线路名简单粗暴,但如果想知道定点区域的详细线路,或许就要一些别的方法了——通过8684网青岛公交获取市区所有路线名,保存到文本中。
(2)稍微修改的代码
直接拿来就能用的代码毕竟是少数啊~~~
import requests
import json
import pandas as pd
import time
from math import sin, asin, cos, radians, fabs, sqrt
#自己写的用于记录时间函数
def record_time(flag):
if flag==0:
global t0
t0=time.time()
else:
t1=time.time()
print("用时:%.2fs"%(t1-t0))
print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))
#python计算两点间直线距离
def Geodistance(lng1,lat1,lng2,lat2):
lng1, lat1, lng2, lat2 = map(radians, [lng1, lat1, lng2, lat2])
dlon=lng2-lng1
dlat=lat2-lat1
a=sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
dis=2*asin(sqrt(a))*6371*1000
return dis
#或许公交信息:线路名、始发站、终点站、行车区间(坐标)、路程、行车区间直线距离
def Bus_inf(city,line):
global bus_num #全局变量,用于计算公交数目
try:
url = 'https://restapi.amap.com/v3/bus/linename?s=rsv3&extensions=all&key=a5b7479db5b24fd68cedcf24f482c156&output=json&city={}&offset=1&keywords={}&platform=JS'.format(city,line)
r = requests.get(url).text
rt = json.loads(r)
dt = {}
dt['line_name'] = rt['buslines'][0]['name'] #公交线路名字
dt['bounds'] = rt['buslines'][0]['bounds'] #行车区间(始发站,终点站坐标)
dt['distance'] = rt['buslines'][0]['distance'] #全程长度
dt['station'] = int(rt['buslines'][0]['busstops'][-1]['sequence']) #全程站点数(包括始发站和终点站)
lng1,lat1 =rt['buslines'][0]['bounds'].split(';')[0].split(',')
lng2,lat2 =rt['buslines'][0]['bounds'].split(';')[1].split(',')
dt['straight_dis'] = Geodistance(float(lng1),float(lat1),float(lng2),float(lat2)) #计算行车区间bounds的直线距离
#if '环形线' in dt['line_name']:
# print(dt['line_name'])
bus_num+=1 #有效公交数+1
return pd.DataFrame(dt,index=[bus_num]) #下标index为“第几条公交线”
except:
print('没有{}公交'.format(line)) #已知路线名的情况下,这条语句不会执行!
return pd.DataFrame() #读取数据失败,返回空的
def Bus_analysis(bus_info):
print('公交线路的平均长度:{:.6f}km'.format(bus_info["distance"].astype(float).mean()))
print('公交线路的平均站点数:{:.6f}个'.format(bus_info['station'].mean()))
#注意:这里的站点数包括了起始站和终点站,求平均站距时每条线路应该去掉一个站!
#正确做法:路程/(求出所有站点数总和-线路数)
print('公交线路的平均站距为:{:.6f}km/站'.format(bus_info["distance"].astype(float).sum()/(bus_info['station'].sum()-bus_num)))
if __name__=="__main__":
record_time(0)
bus_num=0 #在已知文本的情况下,直接输出文本元素个数就是路线数,这里延续以前代码思路,故没修改
all_buslines=pd.DataFrame()
city='青岛市'
#获取已知线路的公交文本
with open("公交线路.txt", "r", encoding="utf-8") as f:
bus_name = f.readlines()
bus_name = bus_name[0].split(",")
for i in bus_name:
all_buslines=pd.concat([all_buslines,Bus_inf(city,i)])
Bus_analysis(all_buslines)
print("通过文本获取青岛市区{}条路线基本信息成功!".format(len(bus_name)))
all_bus.to_csv("通过文本获取的青岛市区{}条路线基本信息.csv".format(len(bus_name)),encoding='utf-8')
record_time(1)
(3)实验结果
对比前面全市找的424条线路(包括不少郊区线),市区线路的平均站距确实会短一点
直接遍历或许会有一些数据的缺失,但要是认真想想,这也可以当作是“随机取样”的过程,而且有的公交线路名字复杂,属于特殊线路,也算是排除异常数据了。
①北京
②突然不想分析了
对比分析见下一篇吧,加上“非直线系数”一块分析。
说明一下,我的这个系列博客很多地方是需要结合着一起来看的!
地球是一个近乎标准的椭球体,它的赤道半径为6378.140千米,极半径为 6356.755千米,平均半径6371.004千米。如果我们假设地球是一个完美的球体,那么它的半径就是地球的平均半径,记为R。如果以0度经线为基准,那么根据地球表面任意两点的经纬度就可以计算出这两点间的地表距离。
按照0度经线的基准,东经取经度的正值(Longitude),西经取经度负值(-Longitude),北纬取90-纬度值(90- Latitude),南纬取90+纬度值(90+Latitude),那么根据三角推导,可以得到计算两点距离的如下公式:
其中lat1与lat2分别为两点的维度,lon1与lon2分别为两点的经度,6378为地球半径,计算的结果为千米,如果要的计算结果为米,在后面乘以1000即可。不过在写代码的时候要将经纬度的值转换为弧度。
def Geodistance(lng1,lat1,lng2,lat2):
lng1, lat1, lng2, lat2 = map(radians, [lng1, lat1, lng2, lat2])
dlon=lng2-lng1
dlat=lat2-lat1
a=sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
dis=2*asin(sqrt(a))*6371*1000
return dis
青岛市区所有线路(注意时效性)
‘1路’,‘11路’,‘12路’,‘12路[区间]’,‘15路’,‘101路’,‘102路’,‘103路’,‘104路’,‘105路’,‘110路[区间]’,‘114路’,‘115路’,‘115路[区间]’,‘116路’,‘117路’,‘19路’,‘120路’,‘121路’,‘125路’,‘126路’,‘128路’,‘129路’,‘130路’,‘123路’,‘110路’,‘131路’,‘111路’,‘119路’,‘16路’,‘122路’,‘103路[定时快车]’,‘102路[定时快车]’,‘18路’,‘2路[汽车]’,‘2路[电车]’,‘20路’,‘21路’,‘22路’,‘23路’,‘24路’,‘25路’,‘26路’,‘28路’,‘29路’,‘205路’,‘206路’,‘207路’,‘208路’,‘209路’,‘210路’,‘213路’,‘214路’,‘215路’,‘216路’,‘218路’,‘219路’,‘220路’,‘221路’,‘222路’,‘223路’,‘224路’,‘225路’,‘226路’,‘227路’,‘228路’,‘229路’,‘230路’,‘231路’,‘232路’,‘233路’,‘216路[定时快车]’,‘3路’,‘30路’,‘31路’,‘32路’,‘36路’,‘302路’,‘303路’,‘304路’,‘307路’,‘308路’,‘310路’,‘312路’,‘313路’,‘314路’,‘316路’,‘317路’,‘318路’,‘319路’,‘320路’,‘321路’,‘322路’,‘361路’,‘362路’,‘363路’,‘364路’,‘326路’,‘366路’,‘367路’,‘368路’,‘369路’,‘370路’,‘371路’,‘372路’,‘373路’,‘374路’,‘375路’,‘378路’,‘381路’,‘380路’,‘379路’,‘382路’,‘385路’,‘325路’,‘383路’,‘384路’,‘386路’,‘387路’,‘327路’,‘363路[定时快车]’,‘318路[定时快车]’,‘363路[大站快车]’,‘318路[大站快车]’,‘302路[定时快车]’,‘313路[定时快车]’,‘321路[定时快车]’,‘374路[定时快车]’,‘388路’,‘389路’,‘390路’,‘4路’,‘405路’,‘403路’,‘402路’,‘401路’,‘461路’,‘462路’,‘463路’,‘465路’,‘407路’,‘408路’,‘409路’,‘410路’,‘466路’,‘411路’,‘467路’,‘468路’,‘469路’,‘470路’,‘412路’,‘413路’,‘406路’,‘404路’,‘414路’,‘471路’,‘415路’,‘5路’,‘501路’,‘503路’,‘502路’,‘503路[定时快车]’,‘605路’,‘610路’,‘611路’,‘612路’,‘613路’,‘616路’,‘615路’,‘618路’,‘619路’,‘620路’,‘621路’,‘623路’,‘622路’,‘625路’,‘629路’,‘630路’,‘627路’,‘631路’,‘632路’,‘626路’,‘628路’,‘633路’,‘635路’,‘636路’,‘614路’,‘624路’,‘637路’,‘639路’,‘638路’,‘640路’,‘641路’,‘634路’,‘642路’,‘643路’,‘644路’,‘7路’,‘761路’,‘765路’,‘762路’,‘763路’,‘766路’,‘767路’,‘769路’,‘770路’,‘771路’,‘772路’,‘768路’,‘773路’,‘774路’,‘775路’,‘772路[区间]’,‘779路’,‘777路’,‘778路’,‘8路’,‘901路’,‘902路’,‘903路’,‘904路’,‘905路’,‘908路’,‘906路’,‘907路’,‘909路’,‘910路’,‘912路’,‘913路’,‘915路’,‘916路’,‘917路’,‘919路’,‘929路’,‘930路’,‘921路’,‘920路’,‘922路’,‘923路’,‘924路’,‘925路’,‘918路’,‘926路’,‘927路’,‘928路’,‘932路’,‘931路’,‘933路’,‘935路’,‘936路’,‘937路’,‘938路’,‘934路’,‘939路’,‘941路’,‘942路’,‘914路’,‘944路’,‘940路’,‘943路’,‘都市观光1线’,‘都市观光4线’,‘高新快线’