12306余票爬虫
1.网页分析
1.1
打开12306余票查询的界面(https://kyfw.12306.cn/otn/leftTicket/init)。普通查询需要输入出发地,目的地和时间,试着输入一下。
1.2
信息更新,页面没有跳转。看下发包情况。
1.3
发现有个包带有’queryX’字段,一看就非常可疑,点开看看。
1.4
ok,这就是我们要的余票信息,不急,先回头解析这余票信息API。
1.5
截去地址前段,留下部分可拆为:
leftTicketDTO.train_date=2017-10-07
&leftTicketDTO.from_station=BJP
&leftTicketDTO.to_station=CQW
&purpose_codes=ADULT
四个参数,用途一目了然。但车站信息是用三位大写字母表示的,包数据里也没有相关内容,有点头疼。
1.6
回到网页找线索。在站点名称输入框出发现值:from_station_name。尝试搜索station_name,有眉目了。
1.7
补上12306的前段地址,看看情况。这密密麻麻的一片,就是我们要的缩写数据。
1.8
材料备齐,开工撸代码。
2.代码实现
2.1
输入:出发地,目的地,时间
输出:各班次的余票信息
过程:将输入信息转化API需要格式——利用API提取各班次信息——分析整理后输出
2.2
根据先前的网页分析,为利用API,我们还需要将站点的汉字名改为对应的三位ABC缩写。对应数据存储于:https://kyfw.12306.cn/otn/resources/js/framework/station_name.js。观察发现,汉字名和ABC缩写的相对位置固定,可以index()汉字位置后,直接切片提取ABC缩写。(12306网站有个自签证书,用requests连接获取数据时需要跳过ssl验证。)
#info_str为网页存储的站点信息。
Station_index = info_str.index("|%s|" %(Station)) +2 +len(Station)
Station_ABC = info_str[Station_index: Station_index + 3]
2.3
尝试将转化后的信息,输入余票信息API,成功(12306有对应频繁获取反制机制,之前次数过多可能无法正常获取信息,要稍等一会)。
2.4
利用python的json库记载余票信息json包。其中我们最关心的余票数据,存储在’data’内的‘result’,格式是一长串字符串,各数据用‘|’间隔开。我们要的做的就是提取之,切割成list方便我们提取对应数据。
#r.text为网页存储的余票json包
ticket_dict = json.loads(r.text)
test_str = a['data']['result']
info_list = tarin_info.split('|')
2.5
写个计数器,遍历一遍每个班次信息的list。多对比下网页的余票内容,我们可以很容易猜到list中各项信息的含义。
2.6
找到我们要的内容,提取出来,按简易格式打印出来,工作就完成啦。
3.个人代码
import requests
import json
def getStationABC(Station):
info_url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js'
r = requests.get(info_url,verify=False)
info_str = r.text
Station_index = info_str.index("|%s|" %(Station)) +2 +len(Station)
Station_ABC = info_str[Station_index: Station_index + 3]
return Station_ABC
def getTicketText(FromStation_ABC,ToStation_ABC,Date):
query_url = "https://kyfw.12306.cn/otn/leftTicket/queryX?\
leftTicketDTO.train_date=%s\
&leftTicketDTO.from_station=%s\
&leftTicketDTO.to_station=%s\
&purpose_codes=ADULT" %(str(Date),FromStation_ABC,ToStation_ABC)
r = requests.get(query_url,verify=False)
ticket_dict = json.loads(r.text)
try:
test_str = ticket_dict['data']['result']
except KeyError:
print('获取信息失败,请稍后重试')
for tarin_info in test_str:
info_list = tarin_info.split('|')
count = 0
print('---- 车次:' + info_list[3] + '-----------------------------')
print('出发时间:' + info_list[8])
print('到站时间:' + info_list[9])
print('旅程时间:' + info_list[10])
print('商务座特等座:'+info_list[32])
print('一等座:' + info_list[31])
print('二等座:' + info_list[30])
print('软卧:' + info_list[23])
print('无座:' + info_list[26])
print('硬卧:' + info_list[28])
print('硬座:' + info_list[29])
if __name__ == '__main__':
FromStation = input('请输入出发地:')
ToStation = input('请输入目的地:')
Date = input('请输入日期(格式:2017-10-09)')
FromStation_ABC = getStationABC(FromStation)
ToStation_ABC = getStationABC(ToStation)
print('目的地:%s;出发地:%s;日期:%s。' %(FromStation,ToStation,Date))
getTicketText(FromStation_ABC,ToStation_ABC,Date)