12306车票查询

12306

从C脱坑开始接触Python,这门传说中的语言
逐渐展示出了它的强大( ॑꒳ ॑)
最近在网上发现有使用python进行12306查票的教程,正好拿来练练手,于是乎写下此文

2018-09-30

重写了查询方式

2018-09-21

由于12306反爬措施频繁更新,无法查询
今后将不定期更新



流程

  1. 获得提交网址
  2. 提交信息
  3. 解析并打印获得信息

需要的模块

  • requests 用于获取网页数据
  • docopt 解析命令行参数
  • prettytable 数据用表格的形式打印在终端
  • colorama 为打印在表格中的数据着色
  • pprint 格式化输出
    安装方式:
    pip install requests prettytable docopt colorama pprint

第一步-获取提交入口

当我们打开网站时,发现只能从页面中的表单进行输入,(另一种方法这里不做讨论)
那么,怎么将数据直接提交给12306的服务器呢?
没错,通过修改网址中的数据来提交,但当然在浏览器中是看不到网址的。
打开谷歌浏览器,在12306查票页面中敲入F12调出开发者工具,找到“Network”按F5刷新(在页面中随便填入查询信息不然无法截获http请求),不出意外就会有资源的请求url、HTTP方法、响应状态码、请求头和响应头及它们各自的值、请求参数等等。
在列表里找到网址:

https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}&leftTicketDTO.to_station={}&purpose_codes=ADULT

{}中需要填入相应数据
比如 {2018-09-19}{SHH}{XAY} 日期只能填写当日及之后30天

那几个字母就是站点的代号,所谓的电报码
显然,12306不愿让我们知道对应的代号,但在通过chrome中强大的f12,同样可以找到对应代号的链接
https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8971

12306_code1.png
不难发现在station_names字符串中有站点对应的中文,因此可以通过程序来获得代号并填入之前的链接

get_stations_code.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import re
import requests
from pprint import pprint
'''
pprint -格式化输出
用于将获得的车站代号并存入station.py文件
'''

url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9081'

#发送get请求,不判断证书
response = requests.get(url, verify=False)

#使用正则表达式提取所有站点 汉字和大写代号
#stations = dict(re.findall(u'([\u4e00-\u9fa5]+)\|([A-Z]+)', response.text))
#使用正则表达式提取所有站点 大写代号和拼音 ([\u0041-\u005a]+)
stationsP = dict(re.findall(u'([A-Z]+)\|([a-z]+)', response.text))
stations = dict( (v,k) for k,v in stationsP.items() )

#格式化输出
pprint(stations,indent=4)

运行程序,输出了站点拼音对应的代号
但如果要获取其中的代号还需要将对应信息放在文件中读取
因此在命令行重定向输出:
bash $python get_stations_code.py > stations.py
这里导入成python文件是为了之后的查询工作,可以直接import进来。

第二步-输入处理

完整程序

tickets.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import requests
import json
import time
import re
from prettytable import PrettyTable
from colorama import Fore,init

title = '车次 出发/到达站 历时 软卧 硬卧 硬座 状态'
nowTime = time.strftime('%Y-%m-%d')
url1 = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8971'
response = requests.get(url1)
stations = re.findall(u'([\u4e00-\u9fa5]+)\|([A-Z]+)',response.text)
stationsPY = re.findall(u'([A-Z]+)\|([a-z]+)',response.text)
stations = dict(stations)
stationsPY = dict({v:k for k,v in stationsPY})
#print(stations)

def name(code):
for k,v in stations.items():
if code == v:
return k

Fs = input('From station:\t')
Ts = input('End station:\t')
F = stations.get(Fs) or stationsPY.get(Fs) or 'SHH'
T = stations.get(Ts) or stationsPY.get(Ts) or 'XAY'
date = input('time of train:\t') or nowTime
if len(date) == 8:
date = '{}-{}-{}'.format(date[:4],date[4:6],date[6:])
elif len(date) == 4:
date = '{}-{}-{}'.format(time.strftime('%Y'),date[:2],date[2:])
elif len(date) == 2:
date = '{}-{}'.format(time.strftime('%Y-%m'),date)
elif len(date) == 1:
date = '{}-{}'.format(time.strftime('%Y-%m'),'0'+date)
print(date,F,T)
#url='https://kyfw.12306.cn/otn/leftTicket/query?leftTicket
url = 'https://kyfw.12306.cn/otn/leftTicket/queryA'
header = {'User-Agent': header = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0'}
key_word = {
"leftTicketDTO.train_date": date,
"leftTicketDTO.from_station": F,
"leftTicketDTO.to_station": T,
"purpose_codes": "ADULT"
}
try:
response = requests.session().get(url, params=key_word, headers=header)
tickets = response.json()
lists = tickets['data']['result']
except Exception as error:
print('404 ?_? this url is losted')
exit(0)
table=PrettyTable(title.split(' '))
#tickets=[]
for list in lists:
#print(list+'\n\n')
ticket = []
lis = list.split('|')
train_code = lis[3]
from_station_name=lis[6]
to_station_name = lis[7]
start_time= lis[8]
arrive_time =lis[9]
lishi = lis[10]
swz_num = lis[32] or lis[25]
zy_num = lis[31]
ze_num = lis[30]
gr_num =lis[21]
rw_num = lis[23]
dw_num = lis[27]
yw_num = lis[28]
rz_num = lis[24]
yz_num = lis[29]
wz_num = lis[26]
qt_num = lis[22]
note_num = lis[1]
#ticket.append()
if lis[11] == 'Y':
lis11 = '有'
elif lis[11] == 'N':
lis11 = '无'
elif lis[11] == 'IS_TIME_NOT_BUY':
lis11 = 'X'

fs = (Fore.GREEN + name(lis[6]) + Fore.RESET)
ts = (Fore.RED + name(lis[7]) + Fore.RESET)
tfs= Fore.GREEN + lis[8] + Fore.RESET
tts= Fore.RED + lis[9] + Fore.RESET
table.add_row([lis[3],fs+'\n'+ts,tfs+'\n'+tts,lis[23],lis[28],lis[29],lis[1]])


print(table)

未完待续
Your encouragement is the driving force of my progress!!
0%