12306.py:
from tkinter import *
import re
import tkinter.messagebox as messagebox
from tkinter import ttk
import time
from Resources import info
from tkinter.ttk import Treeview
from ProApi import *
K = []
D = []
G = []
Z = []
T = []
arguments = {
'-g': '',
'-d': '',
'-z': '',
'-t': '',
'-k': ''
}
tickets = []
pos = []
def GetStation():
clean(K, D, G, Z, T)
del tickets[:]
for item in tree.get_children():
tree.delete(item)
from_station = var_from.get().strip()
to_station = var_to.get().strip()
from_time = number.get()
info = inputArgs(from_station, to_station, from_time)
if info['from_station'] !='' and info['to_station'] != '' and info['flag'] != 'False':
ticket_list = []
countsStr=days.get().strip()
if countsStr=='':
messagebox.showinfo('ERROR', '查询天数必须输入,请输入')
return
counts = int(countsStr.strip())
if counts <= 0:
messagebox.showinfo('ERROR', '查询天数必须大于等于1,请重新输入')
return
else:
ticket_list = operate(counts)
i=0
for item in ticket_list:
if re.match(r'^K', item[1]):
K.append(i)
if re.match(r'^D', item[1]):
D.append(i)
if re.match(r'^G|C', item[1]):
G.append(i)
if re.match(r'^Z', item[1]):
Z.append(i)
if re.match(r'^T', item[1]):
T.append(i)
i = i + 1
length = len(ticket_list)
for i in range(length):
tickets.append(ticket_list[i])
pos = FilterFun()
for i in pos:
tree.insert('', i, values=ticket_list[i])
children = tree.get_children()
for num in range(len(children)):
if (num % 2 == 0):
tree.item(children[num], tags=["evenLine"])
else:
tree.item(children[num], tags=["evenLine2"])
tree.tag_configure("evenLine", background='#DEF1EC')
tree.tag_configure("evenLine2", background='#F1EBDE')
ticket_list = []
def selectall():
if all.get() == '1':
g.set('1')
d.set('1')
z.set('1')
t.set('1')
k.set('1')
else:
g.set('0')
d.set('0')
z.set('0')
t.set('0')
k.set('0')
FilterFun2()
def filterG():
all.set('0')
FilterFun2()
def filterD():
all.set('0')
FilterFun2()
def filterZ():
all.set('0')
FilterFun2()
def filterT():
all.set('0')
FilterFun2()
def filterK():
all.set('0')
FilterFun2()
def cleanTree():
clean(K, D, G, Z, T)
del tickets[:]
for item in tree.get_children():
tree.delete(item)
all.set('0')
selectall()
days.set('1')
def cleanTree2():
for item in tree.get_children():
tree.delete(item)
def FilterFun():
temp = []
if d.get() == '1':
for i in D:
temp.append(i)
if k.get() == '1':
for i in K:
temp.append(i)
if z.get() == '1':
for i in Z:
temp.append(i)
if t.get() == '1':
for i in T:
temp.append(i)
if g.get() == '1':
for i in G:
temp.append(i)
if g.get() == '0' and d.get() == '0' and k.get() == '0' and z.get() == '0' and t.get() == '0':
for i in range(len(tickets)):
temp.append(i)
return temp
def FilterFun2():
cleanTree2()
pos = FilterFun()
for i in pos:
tree.insert('', i, values=tickets[i])
children = tree.get_children()
for num in range(len(children)):
if (num % 2 == 0):
tree.item(children[num], tags=["evenLine"])
else:
tree.item(children[num], tags=["evenLine2"])
tree.tag_configure("evenLine", background='#DEF1EC')
tree.tag_configure("evenLine2", background='#F1EBDE')
def operate(counts):
temp = []
today = date.today()
lastday = today + timedelta(days=15)
from_station = info['from_station']
to_station = info['to_station']
from_date = info['from_date']
mdate = date(int(from_date[:4]), int(from_date[5:7]), int(from_date[8:]))
for i in range(counts):
# 执行查询
ndate = mdate + timedelta(days=i)
if today <= ndate and ndate < lastday:
lists = resolveData(from_station, to_station, ndate)
for item in lists:
temp.append(item)
else:
break
return temp
root = Tk()
root.geometry('1135x600')
root.title('火车票查询系统')
root.iconbitmap('icon.ico') # 更换tk图标
TopFrame = Frame(root, width=1135, height=150)
TLFrame = Frame(TopFrame, width=985, height=150)
TRFrame = Frame(TopFrame, width=150, height=150)
TLTFrame = Frame(TLFrame, width=985, height=75)
TLBFrame = Frame(TLFrame, width=985, height=75)
# 查询信息输入
Label(TLTFrame, text='始发站:', font=('宋体', (11))).pack(side=LEFT)
var_from = StringVar()
from_s = Entry(TLTFrame, textvariable=var_from, width=14).pack(side=LEFT)
Label(TLTFrame, text='终点站:', font=('宋体', (11)),padx=20).pack(side=LEFT)
var_to = StringVar()
to_s = Entry(TLTFrame, textvariable=var_to, width=14).pack(side=LEFT)
# 创建一个下拉列表
Label(TLTFrame, text='出发日期:', font=('宋体', (11)),padx=20).pack(side=LEFT)
number = StringVar()
numberChosen = ttk.Combobox(TLTFrame, width=12, textvariable=number)
numberChosen['values'] = (1, 2, 4, 42, 100) # 设置下拉列表的值
values = []
y = int(time.strftime("%Y", time.localtime()))
m = int(time.strftime("%m", time.localtime()))
d = int(time.strftime("%d", time.localtime()))
i = 0
yy = y
mm = m
dd = d
while i < 15:#15天数据
if m in (1, 3, 5, 7, 8, 10, 12):
if d + i > 31:
dd = d + i - 31
mm = m + 1
if mm > 12:
yy = y + 1
mm = mm - 12
else:
dd = d + i
elif m in (4, 6, 9, 11):
if d + i > 30:
dd = d + i - 30
mm = m + 1
if mm > 12:
yy = y + 1
mm = mm - 12
else:
dd = d + i
else:
if (m % 400 == 0) or ((m % 4 == 0) and (m % 100 != 0)):
if d + i > 29:
dd = d + i - 29
mm = m + 1
if mm > 12:
yy = y + 1
mm = mm - 12
else:
dd = d + i
else:
if d + i > 28:
dd = d + i - 28
mm = m + 1
if mm > 12:
yy = y + 1
mm = mm - 12
else:
dd = d + i
s = '%d-%02d-%02d' % (yy, mm, dd)
values.append(s)
i += 1
numberChosen['values'] = tuple(values)
numberChosen.current(0) # 设置下拉列表默认显示的值,0为 numberChosen['values'] 的下标值
numberChosen.pack(side=LEFT)
Label(TRFrame, text='查询天数:', font=('宋体', (11)),padx=5).pack(side=LEFT)
days = StringVar()
daysEntry = Entry(TRFrame, textvariable=days, width=5).pack(side=LEFT)
days.set('1')
Label(TRFrame, text=' ', font=('宋体', (11)), padx=5).pack(side=LEFT)
search = Button(TRFrame, text='查询余票', width=10, command=GetStation,bg='#8EDEF0').pack(side=LEFT)
Label(TRFrame, text=' ', font=('宋体', (11)), padx=5).pack(side=LEFT)
filterType = Button(TRFrame, text='复 位', width=10, command=cleanTree,bg='#BBEF8A').pack(side=LEFT)
TLFrame.pack(side=LEFT)
TLTFrame.pack(side=TOP)
TLBFrame.pack(side=BOTTOM)
TRFrame.pack(side=RIGHT)
TopFrame.pack(side=TOP, pady=5)
# 车型选择
Label(TLBFrame, text='车次类型:', font=('宋体', (11))).pack(side=LEFT)
all = StringVar()
g = StringVar()
d = StringVar()
z = StringVar()
t = StringVar()
k = StringVar()
Checkbutton(TLBFrame, text = '全部',padx=10,variable = all,command=selectall).pack(side=LEFT)
Checkbutton(TLBFrame, text = 'G/C-高铁/城际',padx=10, command=filterG, variable = g).pack(side=LEFT)
Checkbutton(TLBFrame, text = 'D-动车',padx=10,variable = d,command=filterD).pack(side=LEFT)
Checkbutton(TLBFrame, text = 'Z-直达',padx=10,variable = z,command=filterZ).pack(side=LEFT)
Checkbutton(TLBFrame, text = 'T-特快',padx=10,variable = t,command=filterT).pack(side=LEFT)
Checkbutton(TLBFrame, text = 'K-快车',padx=10,variable = k,command=filterK).pack(side=LEFT)
all.set('0')
d.set('0')
g.set('0')
t.set('0')
k.set('0')
z.set('0')
# 车票展示
ShowFrame = Frame(root, width=780, height=450, bg='pink')
ShowFrame.pack(side=TOP)
scrollBar = Scrollbar(ShowFrame)
scrollBar.pack(side=RIGHT,fill=Y)
tree = Treeview(ShowFrame,
columns=('c0','c1','c2','c3','c4','c5','c6','c7','c8','c9','c10','c11','c12','c13','c14','c15','c16'),
show="headings",
height=540,
yscrollcommand=scrollBar.set)
tree.column('c0',width=100,anchor='center')
tree.column('c1',width=50,anchor='center')
tree.column('c2',width=100,anchor='center')
tree.column('c3',width=100,anchor='center')
tree.column('c4',width=50,anchor='center')
tree.column('c5',width=60,anchor='center')
tree.column('c6',width=60,anchor='center')
tree.column('c7',width=60,anchor='center')
tree.column('c8',width=60,anchor='center')
tree.column('c9',width=60,anchor='center')
tree.column('c10',width=60,anchor='center')
tree.column('c11',width=60,anchor='center')
tree.column('c12',width=60,anchor='center')
tree.column('c13',width=60,anchor='center')
tree.column('c14',width=60,anchor='center')
tree.column('c15',width=60,anchor='center')
tree.column('c16',width=60,anchor='center')
#显示车次,出发/到达站,出发/到达时间,历时,商务座,一等座,二等座,软卧,动卧,硬卧,硬座,无座
tree.heading('c0',text='出发日期')
tree.heading('c1',text='车次')
tree.heading('c2',text='出发/到达站')
tree.heading('c3',text='出发/到达时间')
tree.heading('c4',text='历时')
tree.heading('c5',text='商务座')
tree.heading('c6',text='一等座')
tree.heading('c7',text='二等座')
tree.heading('c8',text='高级软卧')
tree.heading('c9',text='软卧')
tree.heading('c10',text='动卧')
tree.heading('c11',text='硬卧')
tree.heading('c12',text='软座')
tree.heading('c13',text='硬座')
tree.heading('c14',text='无座')
tree.heading('c15',text='其他')
tree.heading('c16',text='备注')
tree.pack(side=LEFT,fill=BOTH)
root.resizable(width=False,height=False)
root.mainloop()
ProAPI.py:包含需要使用的一些我封装好的API
import re, urllib.parse, urllib.request, http.cookiejar, json, datetime, time
from stationsInfo import stationLists, stations2CN, stations2CODE
from prettytable import PrettyTable
from datetime import timedelta, date
import tkinter.messagebox as messagebox
from Resources import *
"""获取数据"""
def getData(url):
data = ''
while 1:
try:
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0'}
req = urllib.request.Request(url=url, headers=headers)
data = urllib.request.urlopen(req).read().decode('utf-8')
# print(url)
# print(data)
# if data['status'] == False:
# break
if data.startswith(u'\ufeff'):
data = data.encode('utf8')[3:].decode('utf-8')
break
except:
continue
return data
"""解析响应数据"""
def resolveData(from_station, to_station, date):
#拼接出查询链接
url = 'https://kyfw.12306.cn/otn/leftTicket/queryA?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}&leftTicketDTO.to_station={}&purpose_codes=ADULT'.format(date, stations2CODE[from_station], stations2CODE[to_station])
#获取数据
while 1:
try:
data = getData(url)
lists = json.loads(data)["data"]["result"]
# if data['status'] == False:
# print('获取失败!请检查网络')
# break
break
except:
continue
cont = []
for items in lists:
data = {
"station_train_code": '',
"from_station_name": '',
"to_station_name": '',
'start_time': '',
'end': '',
"lishi": '',
"swz_num": '',
"zy_num": '',
"ze_num": '',
"dw_num": '',
"gr_num": '',
"rw_num": '',
"yw_num": '',
"rz_num": '',
"yz_num": '',
"wz_num": '',
"qt_num": '',
"note_num": ''
}
item = items.split('|')
data['station_train_code'] = item[3]
data['from_station_name'] = item[6]
data['to_station_name'] = item[7]
data['start_time'] = item[8]
data['arrive_time'] = item[9]
data['lishi'] = item[10]
data['swz_num'] = item[32] or item[25]# 商务座在32或25位置
data['zy_num'] = item[31]
data['ze_num'] = item[30]
data['gr_num'] = item[21]
data['rw_num'] = item[23]
data['dw_num'] = item[27]
data['yw_num'] = item[28]
data['rz_num'] = item[24]
data['yz_num'] = item[29]
data['wz_num'] = item[26]
data['qt_num'] = item[22]
if item[0] == 'null' or item[0] == '' or item[1] == '列车运行图调整,暂停发售':
data['note_num'] = '不可预订'
else:
data['note_num'] = item[1]
train_no = item[2]
from_station_no = item[16]
to_station_no = item[17]
types = item[35]
for pos in name:
if data[pos] == '':
data[pos] = '-'
cont.append(data)
tickets = []
for x in cont:
tmp = []
for y in name2:
if y == "from_station_name":
s = stations2CN[x[y]] + '--' + stations2CN[x["to_station_name"]]
tmp.append(s)
elif y == "start_time":
s = x[y] + '--' + x["arrive_time"]
tmp.append(s)
elif y == "station_train_code":
s = x[y]
tmp.append(s)
elif y == 'date':
tmp.append(date.strftime('%Y-%m-%d'))
else:
tmp.append(x[y])
tickets.append(tmp)
return tickets
def clean(K,D,G,Z,T):
del K[:]
del D[:]
del Z[:]
del G[:]
del T[:]
#检查输入信息from_station, to_station, d
def inputArgs(from_station, to_station, d):
# 校验
flag1 = False
flag2 = False
from_index = stationLists.count(from_station)
to_index = stationLists.count(to_station)
# 始发站在车站列表中,并且始发站和终点站不同
if from_index > 0 and to_station != from_station:
flag1 = True
# 终点站在车站列表中,并且始发站和终点站不同
if to_index > 0 and to_station != from_station:
flag2 = True
if not flag1:
info['flag'] = 'False'
messagebox.showinfo('ERROR', '输入的出发站有误,请重新输入')
if not flag2:
info['flag'] = 'False'
messagebox.showinfo('ERROR', '输入的终点站有误,请重新输入')
info['from_station'] = from_station
info['to_station'] = to_station
info['from_date'] = d
return info
代码中涉及的字典、列表等:
Resources.py:
data = {
"station_train_code": '',
"from_station_name": '',
"to_station_name": '',
'start_time': '',
'end': '',
"lishi": '',
"swz_num": '',
"zy_num": '',
"ze_num": '',
"dw_num": '',
"gr_num": '',
"rw_num": '',
"yw_num": '',
"rz_num": '',
"yz_num": '',
"wz_num": '',
"qt_num": '',
"note_num": ''
}
name2 = [
"date",
"station_train_code",
"from_station_name",
'start_time',
"lishi",
"swz_num",
"zy_num",
"ze_num",
"gr_num",
"rw_num",
"dw_num",
"yw_num",
"rz_num",
"yz_num",
"wz_num",
"qt_num",
"note_num"
]
name = [
"station_train_code",
"from_station_name",
'start_time',
"lishi",
"swz_num",
"zy_num",
"ze_num",
"gr_num",
"rw_num",
"dw_num",
"yw_num",
"rz_num",
"yz_num",
"wz_num",
"qt_num",
"note_num"
]
info = {
'from_station': '',
'to_station': '',
'from_date': '',
'flag':''
}
pricesDic = {
'A': '',
'B': '',
'C': '',
'D': '',
'E': '',
'F': '',
'G': '',
'H': '',
'I': '',
'J': ''
}
priceName = [
"swz_num",
"dw_num",
"zy_num",
"ze_num",
"gr_num",
"rw_num",
"yw_num",
"rz_num",
"yz_num",
"wz_num"
]
还有全国车站列表,用于将车站拼音、中文、简拼—>车站代码和车站代码—>中文的stationsInfo.py:
https://pan.baidu.com/s/1i4FTjgD