You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

201 lines
7.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import tkinter as tk #创建图形用户界面的库
from tkinter import ttk #提供一些更为现代的组件
from tkinter import messagebox #提供一个用于显示消息框的简单接口
import requests #用于进行http请求来获取请12306网站数据
import prettytable as pt#用来创建漂亮的文本表格
import json#处理JSON数据来获取城市车站信息
import datetime#处理日期和时间
# 验证用户输入的城市名称和日期是否有效
def validate_inputs(from_city, to_city, date):
"""验证用户输入的城市名称和日期是否有效"""
# 检查城市名称是否为非空字符串
if not from_city or not to_city:
messagebox.showerror("错误", "城市名称不能为空。")
return False
# 检查日期是否为有效的YYYY-MM-DD格式
if not is_valid_date(date):
messagebox.showerror("错误", "日期格式错误,应为 YYYY-MM-DD。")
return False
return True
def is_valid_date(date_str):
"""检查日期字符串是否为有效的YYYY-MM-DD格式"""
try:
# 尝试将字符串转换为日期
datetime.datetime.strptime(date_str, '%Y-%m-%d')
return True
except ValueError:
return False
# 根据用户输入的出发城市、目的城市和日期来搜索列车
def search_trains():
from_city = from_city_combobox.get()
to_city = to_city_combobox.get()
date = date_entry.get()
train_type = train_type_combobox.get()
# 验证输入:用户输入的出发城市、目的城市和日期无效,则停止执行 search_trains 函数,并返回 None。
if not validate_inputs(from_city, to_city, date):
return
# 读取城市文件 -> 返回json字符串
try:
with open('city.json', 'r', encoding='utf-8') as f:
city_data = json.load(f)
except FileNotFoundError:
messagebox.showerror("错误", "城市数据文件不存在,请确保 city.json 文件在当前目录下。")
return
except json.JSONDecodeError:
messagebox.showerror("错误", "城市数据文件格式错误,请确保 city.json 文件内容为有效的 JSON 格式。")
return
# 构造URL
url = f'https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={date}&leftTicketDTO.from_station={city_data[from_city]}&leftTicketDTO.to_station={city_data[to_city]}&purpose_codes=ADULT'
# 向指定的URL发送一个带有特定头信息的GET请求并获取服务器的响应
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0',
'Cookie': '_uab_collina=171195922365279549520858; JSESSIONID=19E602A91EBCFCD88A8BBDF5DABDE80F; _jc_save_wfdc_flag=dc; _jc_save_fromStation=%u682A%u6D32%2CZZQ; _jc_save_toStation=%u957F%u6C99%2CCSQ; BIGipServerotn=1978138890.24610.0000; BIGipServerpassport=837288202.50215.0000; guidesStatus=off; highContrastMode=defaltMode; cursorStatus=off; route=495c805987d0f5c8c84b14f60212447d; _jc_save_fromDate=2024-05-27; _jc_save_toDate=2024-05-27'
}
response = requests.get(url=url, headers=headers)
# 检查请求状态
if response.status_code != 200: # 判断http响应状态码是否成功
messagebox.showerror("错误", f"请求失败,状态码: {response.status_code}")
return
# 解析数据
try:
json_data = response.json()
result = json_data['data']['result']
except (KeyError, json.JSONDecodeError):
messagebox.showerror("错误", "解析数据失败,可能是服务器返回格式错误。")
return
# 创建 PrettyTable 表格
tb = pt.PrettyTable()
tb.field_names = [
'序号',
'车次',
'出发时间',
'到达时间',
'耗时',
'特等座',
'一等',
'二等',
'软卧',
'硬卧',
'硬座',
'无座',
]
page = 1
# 填充表格数据
for i in result:
index = i.split('|')
num = index[3]
#根据车次类型进行筛选
train_num = index[3]
start_time = index[8]
end_time = index[9]
use_time = index[10]
topGrade = index[32]
first_class = index[31]
second_class = index[30]
hard_sleeper = index[28]
hard_seat = index[29]
no_seat = index[26]
soft_sleeper = index[23]
# 根据车次类型筛选
if train_type != "全部" and train_type not in train_num:
continue
tb.add_row([
page,
num,
start_time,
end_time,
use_time,
topGrade,
first_class,
second_class,
soft_sleeper,
hard_sleeper,
hard_seat,
no_seat,
])
page += 1
# 显示表格
result_text.config(state=tk.NORMAL)
result_text.delete('1.0', tk.END)
result_text.insert(tk.END, str(tb))
result_text.config(state=tk.DISABLED)
# 创建主窗口
root = tk.Tk()
root.title("火车票查询")
# 创建车次类型选择的下拉列表
train_type_label = ttk.Label(root, text="车次类型:")
train_type_label.grid(row=3, column=0, padx=5, pady=5, sticky="e")
train_type_combobox = ttk.Combobox(root, values=["全部", "G","S","D", "T", "K"])
train_type_combobox.grid(row=3, column=1, padx=5, pady=5)
train_type_combobox.current(0)
# 创建出发城市输入框和标签
from_city_label = ttk.Label(root, text="出发城市:")
from_city_label.grid(row=0, column=0, padx=5, pady=5, sticky="e")
from_city_combobox = ttk.Combobox(root, values=[])
from_city_combobox.grid(row=0, column=1, padx=5, pady=5)
# 创建目的城市输入框和标签
to_city_label = ttk.Label(root, text="目的城市:")
to_city_label.grid(row=1, column=0, padx=5, pady=5, sticky="e")
to_city_combobox = ttk.Combobox(root, values=[])
to_city_combobox.grid(row=1, column=1, padx=5, pady=5)
# 创建出发日期输入框和标签
date_label = ttk.Label(root, text="出发日期:")
date_label.grid(row=2, column=0, padx=5, pady=5, sticky="e")
date_entry = ttk.Entry(root)
date_entry.grid(row=2, column=1, padx=5, pady=5)
# 创建查询按钮
search_button = ttk.Button(root, text="查询", command=search_trains)
search_button.grid(row=10, columnspan=2, padx=5, pady=5)
# 创建显示结果的文本框
result_text = tk.Text(root, height=20, width=100)
result_text.grid(row=4, columnspan=2, padx=5, pady=5)
result_text.config(state=tk.DISABLED)
# 创建欢迎文本框
welcome_label = ttk.Label(root, text="欢迎来到cb的查票系统")
# 设置字体大小
welcome_label.config(font=("微软雅黑", 20)) # 这里使用了微软雅黑字体字体大小为20
welcome_label.grid(row=0, column=0, padx=5, pady=5, sticky="w")# 添加到左上角
# 更新城市下拉列表
def update_city_comboboxes():
cities = list(city_data.keys())
cities.sort()
from_city_combobox['values'] = cities
to_city_combobox['values'] = cities
# 初始化城市数据
try:
with open('city.json', 'r', encoding='utf-8') as f:
city_data = json.load(f)
update_city_comboboxes()
except FileNotFoundError:
messagebox.showerror("错误", "城市数据文件不存在,请确保 city.json 文件在当前目录下。")
except json.JSONDecodeError:
messagebox.showerror("错误", "城市数据文件格式错误,请确保 city.json 文件内容为有效的 JSON 格式。")
root.mainloop()