python爬虫——12306查询车次

  • 使用抓包工具
  • 中文地名转地名参数代码
  • 使用12306提供的查询api进行查询
  • 实现完整代码


使用抓包工具

首先登陆12306 点击查询:同时使用network抓包:

可以观察到查询结果来自于12306 所提供的一个api。

基于python的火车票管理系统 python火车票爬虫_12306


发现需要地名参数,那么怎么获得呢?在source中的js文件发现:

基于python的火车票管理系统 python火车票爬虫_爬虫_02

中文地名转地名参数代码

url1=‘https://kyfw.12306.cn/otn/resources/js/framework/station_name.js

这是一个js文件。

这是一个12306解析中文地名对应的地名参数的网址。

爬取信息:得到一个很长var,各项信息通过@分隔。

基于python的火车票管理系统 python火车票爬虫_3D_03


处理后,得到每项这样的数据:

>>> 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)

通过上述链接:

基于python的火车票管理系统 python火车票爬虫_基于python的火车票管理系统_04


得到了一个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()