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