python爬虫——12306查询车次
- 使用抓包工具
- 中文地名转地名参数代码
- 使用12306提供的查询api进行查询
- 实现完整代码
使用抓包工具
首先登陆12306 点击查询:同时使用network抓包:
可以观察到查询结果来自于12306 所提供的一个api。
发现需要地名参数,那么怎么获得呢?在source中的js文件发现:
中文地名转地名参数代码
url1=‘https://kyfw.12306.cn/otn/resources/js/framework/station_name.js’
这是一个js文件。
这是一个12306解析中文地名对应的地名参数的网址。
爬取信息:得到一个很长var,各项信息通过@分隔。
处理后,得到每项这样的数据:
>>> inf [:1]
['bjb|北京北|VAP|beijingbei|bjb|0']
对如上列表进一步处理,提取字符串,然后按|分隔。得到:
['bjb', '北京北', 'VAP', 'beijingbei', 'bjb', '0']
得到如上信息,将VAP作为key值,北京北、beijingbei、bjb作为属性。
构造一个stations字典。
stations[rlist[2]]={"cn":rlist[1],"qp":rlist[3],"jp":rlist[4]}
{'VAP': {'cn': '北京北', 'qp': 'beijingbei', 'jp': 'bjb'}}
可以观察到,我们构成了一个嵌套字典。
对于stations获取items取值得到一个字典station。
通过我们输入站点的关键字,与词典中匹配,得到一系列站点供我们选择。
选择后,返回该站点的英文代码。
使用12306提供的查询api进行查询
一共需要3个参数:起始站,终点站,出发日期。
qurl="https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}&leftTicketDTO.to_station={}&purpose_codes=ADULT".format(chufatime,fromstation,tostation)
通过上述链接:
得到了一个json文件:
将json数据取data,result。
data给出是我们提供给它的地点,对应的站点信息。
其中result返回了我们需要的信息:
如:每条数据
'ZTRMtKVBVFYm3d5Kjup163v3OSMmUy%2BPrskGUm9kTOI6W%2BqIdwIpfbBiGb5BI67DqUK5LIcW6Awi%0AR8VVpa4Ak7mZc6Q9NVTdUI3NwjCOtrP7G9MYY2oAYeSIBEVGNSxXYXpRJlE6itmTmrnPCF%2F7YmA6%0AvO4D%2FcJ5L3ETAw8I58caT5mMhGpIPT2gjEXT132ivNyYC8gOsDcng1pupzlzUelInuSeb%2F8yQ4af%0AxGnecxDqOowWEr23KgxcUG0sKSGN5wnFBWOCfDNwVtnUdZcRuiZYkez2bZozSbvfWbz%2FZM41ACaa%0A%2FRXrIA%3D%3D|预订|0400000K500D|K47|QHX|HZH|NJH|SNH|01:39|06:00|04:21|Y|EXRRSMFtMXw0snZWYzcnmht2UMoF5gPzI2IzlyOzEbpaovq6aGTBSUpmRmY%3D|20190323|3|B1|37|43|0|0||||无|||有||无|无|||||10401030|1413|0|0|null'
按|划分,然后观察得出需要的属性:
第一项是随机生成的字符串,不知道有什么用。
- K47(3) :为车次
- NJH(6) :起始站
- NJH(7) :终点站
- 01:39 (8) :发车时间
- 06:00 (9) :到站时间
- 04:21 (10) :行车时长
- 无(23) :软卧
- 有(26) :硬卧
- 无(28) :无座
- 无(29) :硬座
checi=list[3]
chufa=stations[list[6]]["cn"]
mudi=stations[list[7]]["cn"]
ftime=list[8]
dtime=list[9]
sw=list[32]
yd=list[31]
rw=list[23]
yw=list[26]
wuzuo=list[28]
ed=list[30]
yz=list[29]
实现完整代码
#python 火车票信息的查询
import requests
# 中文地名转英文码
url1="https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9090"
txt=requests.get(url1).text
inf=txt[:-2].split("@")[1:]
#print(inf)
stations={}
for record in inf:
rlist=record.split("|")
stations[rlist[2]]={"cn":rlist[1],"qp":rlist[3],"jp":rlist[4]} #把车站编码当作key
#print(stations)
def getcode(t):
while True:
s1=input("%s站:"%t)
r1=[]
for id,station in stations.items():
# 判断字典中是否有我们输入的值
if s1 in station.values():
r1.append((id,station))
if r1: #添加了一系列的站点
break
print("没有这个车站。")
print("请重新输入。")
if len(r1)==1: # 如果仅有一个站,可以直接取代码值
sid=r1[0][0]
else:
# 有多个匹配的站,需要选择
print("你需要在以下车站里选择:")
for i in range(len(r1)):
print(i+1,r1[i][1]["cn"])
sel=int(input("你的选择是:"))-1
sid=r1[sel][0]
return sid # 返回站点的英文代码
fromstation=getcode("出发")
tostation=getcode("到达")
chufatime=input("出发日期(格式2019-01-01):").strip()
qurl="https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}&leftTicketDTO.to_station={}&purpose_codes=ADULT".format(chufatime,fromstation,tostation)
print(qurl)
print("你输入的查询条件是:出发站=%s,到达站=%s"%(stations[fromstation]["cn"],stations[tostation]["cn"]))
ainf=requests.get(qurl).json()["data"]["result"] #json文件存储当前从出发站到目的站的所有车次的详细信息
#print(ainf,type(ainf))
result=[]
for i in ainf:
list=i.split("|")
checi=list[3]
chufa=stations[list[6]]["cn"]
mudi=stations[list[7]]["cn"]
ftime=list[8]
dtime=list[9]
sw=list[32]
yd=list[31]
rw=list[23]
yw=list[26]
wuzuo=list[28]
ed=list[30]
yz=list[29]
result.append((checi,chufa,mudi,ftime,dtime,sw,yd,ed,yz,yw,rw,wuzuo))
#print(result)
print("车次\t出发站\t到达站 出发时间 到达时间 商务座 一等座 二等座 硬座 硬卧 软卧 无座 ")
for i in result:
for n in range(len(i)):
print(i[n],end="\t")
print()