学了一学期Python还是让我感受到这是一门强大的语言,如果能熟练使用许多库的话,它给人带来的方便是非常大的,
很难想象一位Freelancer当初单枪匹马搞出来的语言,如今能发展壮大到这个程度!
本着服务生活的态度为Python的期末大作业做了一个能够查询连续多日内某地到某地的票源情况的查票器,
由于是脚本跳过了浏览器的限制,直接用URL(
Universal Resource Locator)向12306服务器索取票池信息,
并且支持多日查询,在票源紧俏时能省下一些花在查询上的时间。
当然,系统这一关还得过,由于缺省状态下12306的根证书是不受系统信任的,使用本脚本前需要先将系统环境设置为信任12306的根证书,不然系统防火墙会自动拦下12306反馈的json包导致无法解析(当然如果不这样做,就是用浏览器上12306也可能出问题的)
参照http://jingyan.baidu.com/article/ce436649e4aa5d3772afd367.html这个上面说的做就行

弄完打开后的图形界面很直观,使用起来应该没问题...
p.s.  其实在查手册的时候还偶然发现存在scrapy这样比urllib更优秀的库可以用来写爬虫,还有PyQt5这样更加美观的GUI模块,这个程序算是对爬虫的一个入门吧,如果以后想写更高级的爬虫,最好还是先学习其他第三方库吧,要学的东西还多着那。  

import urllib.request,json,re,time #urllib用于抓包 json用于解析 re用于匹配车站名称 time用于获取本地时间
import tkinter as tk        #用于基本的gui窗口
import tkinter.ttk as ttk   #用于需求美观的gui widgets
#使用本程序前请先将系统环境设置为信任12306的根证书,以保证12306返回的json不会被系统拦截
def getStationNames(from_station,to_station):           #获取车站名称信息,输入为汉字,输出为12306可读的标识符url='https://kyfw.12306.cn/otn/resources/js/framework/station_name.js' #需要抓取的包来自这个URLreq=urllib.request.Request(url)                     #建立请求对象response=urllib.request.urlopen(req)                #发送请求并获取服务器的反馈data=response.read()                                #读取反馈信息,其中记录了汉字与车站标识符的对应关系try:data=data.decode('utf-8')                       #尝试解码 首先使用utf-8格式except:data=data.decode('gbk','ignore')                #失败则强制使用汉字编码模式from_station=re.findall('%s\|([^|]+)' % from_station,data)[0]   #正则匹配发站标识符to_station=re.findall('%s\|([^|]+)' % to_station,data)[0]       #正则匹配到站标识符return from_station,to_station                      #返回匹配结果class TicketPool:                       #这个类记录了“某一日某车次从某地到某地”的票池def __init__(self):self.station_train_code=''      #列车号self.start_train_date=''        #发车日期self.from_station_name=''       #出发站self.to_station_name=''         #目的站self.lishi=''                   #历时self.start_time=''              #开始时间self.arrive_time=''             #到达时间self.swz_num=''                 #商务座数self.tz_num=''                  #特等座数self.zy_num=''                  #一等座数self.ze_num=''                  #二等座数self.gr_num=''                  #高级软卧数self.rw_num=''                  #软卧数self.yw_num=''                  #硬卧数self.rz_num=''                  #软座数self.yz_num=''                  #硬座数self.wz_num=''                  #无座数self.qt_num=''                  #其他座数def getTicketPools(queryDate,from_station,to_station): #本函数接收“发车日”“起始地”“目的地”,返回“那一天” 所有 从“起始地”到“目的地”的票池集合#根据“发车日”“起始地”“目的地”生成12306可识别的查票URL,发送至服务器端并接收反馈,将反馈解析成票池的集合url='https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=0X00&queryDate=%s&from_station=%s&to_station=%s'%(queryDate,from_station,to_station) req=urllib.request.Request(url)      #dittoresponse=urllib.request.urlopen(req) #dittodata=response.read()                 #dittotry:                                 #dittodata=data.decode('utf-8')        #dittoexcept:                              #dittodata=data.decode('gbk','ignore') #dittotry:trains=json.loads(data)["data"]["datas"] #服务器端返回一个json包,首先需要尝试将其解包except:                                      #由于直接解包后获得的是一个字典,其中包含多个列表,列表的中元素是包含车票信息的字典,trains=[]                                #经过分析我们不需要解包所有值,只需要其中["data"]["datas"]部分TicketPools=[]                               #如果解包失败说明包空无票,将trains作一个空表,也就使得TicketPools最终为空for train in trains:                        #将要存储到TicketPools中的内容是tmp=TicketPool()                        #每一个字典生成的TicketPool,它来自于解包得到的列表中的每个字典包含的车票信息tmp.station_train_code=train["station_train_code"]      #列车号tmp.start_train_date=train["start_train_date"]        #发车日期tmp.from_station_name=train["from_station_name"]       #出发站tmp.to_station_name=train["to_station_name"]         #目的站tmp.lishi=train["lishi"]                   #历时tmp.start_time=train["start_time"]              #开始时间tmp.arrive_time=train["arrive_time"]             #到达时间tmp.swz_num=train["swz_num"]                 #商务座数tmp.tz_num=train["tz_num"]                  #特等座数tmp.zy_num=train["zy_num"]                  #一等座数tmp.ze_num=train["ze_num"]                  #二等座数tmp.gr_num=train["gr_num"]                  #高级软卧数tmp.rw_num=train["rw_num"]                  #软卧数tmp.yw_num=train["yw_num"]                  #硬卧数tmp.rz_num=train["rz_num"]                  #软座数tmp.yz_num=train["yz_num"]                  #硬座数tmp.wz_num=train["wz_num"]                  #无座数tmp.qt_num=train["qt_num"]                  #其他座数TicketPools.append(tmp)                     #存入TicketPools return TicketPools                              #返回TicketPoolsdef is_leap_year(year): #判断是否为闰年 为日期调换作准备if (year%4==0 and year%100!=0) or year%400==0:  #是4且不是100的整倍数 或者是400的整倍数return True #是闰年else:         #否则return False #不是def next_day(today):  #输入一个12306可识别的日期标识符,输出下一天的日期标志符[yr,mon,day]=today.split('-')  #利用标识符的-号将其分为年月日三部分months=[31,28,31,30,31,30,31,31,30,31,30,31] #存储每个月天数if(is_leap_year(int(yr))): #闰年months[1]=29            #2月有29天if(int(day)+1>months[int(mon)-1]):          #跨月if(int(mon)==12):                        #跨年mon='01'              #变为01月day='01'              #变为01日yr=str(int(yr)+1)     #年数加一else:                                   #不跨年mon=str(int(mon)+1).zfill(2)       #月数加一并补零填充为2位day='01'                          #日期为01号else:                                       #不跨月day=str(int(day)+1).zfill(2)          #不跨月只需将日期加一并补零为2位tomorrow="%s-%s-%s"%(yr,mon,day)          #将年月日重新粘合为标识符return tomorrow                         #返回下一天的标识符def getTicketStack(from_station,to_station,start_date,end_date): #本函数通过接受起站、止站、起日和止日获取这一时期内每一天票池集合的集合from_station,to_station=getStationNames(from_station,to_station)    #得到正确的起站、止站标识符TicketStack=[]  #建立集合while True:     if start_date==end_date: #从起日开始 判断是否到达止日TicketStack.append(getTicketPools(start_date,from_station,to_station)) #如果到达止日 将这一天的票池存储break #而后退出TicketStack.append(getTicketPools(start_date,from_station,to_station)) #如果没到达止日 将这一天的票池存储start_date=next_day(start_date)  #而后取下一日return TicketStack #返回票池集合的集合class User_Interface:   #用户界面def __init__(self): #初始化self.root=tk.Tk() #tk窗口self.root.geometry("650x400") #默认大小self.root.title("查票器V1.0") #注明功能和版本menubar = tk.Menu(self.root)self.root["menu"]=menubarhelpmenu = tk.Menu(menubar, tearoff=0)helpmenu.add_command(label="帮助",command=self.morehelp)helpmenu.add_command(label="关于",command=self.moreabout)helpmenu.add_separator()helpmenu.add_command(label="退出", command=self.root.destroy)menubar.add_cascade(label="更多", menu=helpmenu)inputframe=tk.Frame(self.root) #输入部分放入一个Frameinputframe.pack()               #显示Frametk.Label(inputframe,text="查票器V1.0").grid(row=0,column=2) #注释tk.Label(inputframe,text="出发站:").grid(row=1,column=0,pady=1) #出发站输入界面注释self.from_station_entry=ttk.Entry(inputframe) #出发站采用entry接收self.from_station_entry.grid(row = 1, column = 1,pady=1) #显示这个entryself.startdays=[]  #可选出发时间列表 today=time.strftime("%Y-%m-%d", time.localtime()) #获取系统的今日日期并转化为12306可识别模式for i in range(60): #12306的预售期为60天self.startdays.append(today) #加入这天的日期today=next_day(today) #变为下一天self.start_date=tk.StringVar() #建立监控起日的变量self.start_date_box=ttk.Combobox(inputframe,textvariable=self.start_date,values=self.startdays,height=6,width=14) #起日采用combobox接收tk.Label(inputframe,text="起始日期:").grid(row=1,column=2,pady=2) #显示labelself.start_date_box.set(self.startdays[0]) #初始为今天的日期self.start_date_box.grid(row = 1, column=3,pady=1) #显示comboboxtk.Label(inputframe,text="目的站:").grid(row=2,column=0,pady=1) #注释self.to_station_entry=ttk.Entry(inputframe) #目的站用entry接收self.to_station_entry.grid(row = 2, column = 1,pady=3) #显示这个entryself.end_date=tk.StringVar() #建立监控止日的变量self.end_date_box=ttk.Combobox(inputframe,textvariable=self.end_date,values="输入起日",height=6,width=14, postcommand =lambda:self.setenddays())#止日列表的初始日期取决于起日tk.Label(inputframe,text="结束日期:").grid(row=2,column=2,pady=2) #显示labelself.end_date_box.set(self.start_date.get()) #初始为起日日期self.end_date_box.grid(row = 2, column = 3,pady=3) #显示combobox runbutton=ttk.Button(inputframe,text='查询',command=lambda: self.submit()) #设置完成输入后的查询按钮runbutton.grid(row=1,column=4,padx=8,rowspan=2) #显示按钮outputframe=tk.Frame(self.root) #输出部分装入一个Frameoutputframe.pack(padx=10) #显示这个Frametitles=("列车号","发车日期","出发站","目的站","历时","发车时间","到达时间","商务座","特等座",   "一等座","二等座","高级软卧","软卧","硬卧","软座","硬座","无座","其他")  #需要显示的车票信息self.ticketstack_tree= ttk.Treeview(outputframe,columns=titles) #建立表格的根节点(实质上表格是一个树)self.ticketstack_tree.pack() #显示表格self.ticketstack_tree['show'] = 'headings' #只显示根节点的子节点,以隐藏空的第一列for each in titles:  #每个车票信息self.ticketstack_tree.column(each,width=80,anchor='center') #有专门的一列self.ticketstack_tree.heading(each,text=each) #显示文本为本身vsb=ttk.Scrollbar(outputframe) #左右拖动滑块vsb["orient"]='horizon' #设置为水平vsb.pack(fill = 'x') #x方向补满self.ticketstack_tree["xscrollcommand"]=vsb.set #将表格的水平控制交由vsb滑块vsb['command']=self.ticketstack_tree.xview #将vsb滑块的控制对象设置为表格的水平视角self.root.mainloop() #进入事件循环def submit(self): #查询按钮的控制函数from_station=self.from_station_entry.get() #获取起站to_station=self.to_station_entry.get() #获取止站start_date=self.start_date.get() #获取起日end_date=self.end_date.get() #获取止日self.trains=0 #查到的列车总数for each in self.ticketstack_tree.get_children(): #先清空原先的表格self.ticketstack_tree.delete(each) #即把表格根节点的每一个子节点删除ticketstack=getTicketStack(from_station,to_station,start_date,end_date) #获取票池集合的集合for eachpools in ticketstack: #对每一个票池集合for eachpool in eachpools: #当中的每一个票池tabulater=(eachpool.station_train_code, eachpool.start_train_date, eachpool.from_station_name, eachpool.to_station_name, eachpool.lishi,eachpool.start_time, eachpool.arrive_time, eachpool.swz_num, eachpool.tz_num, eachpool.zy_num, eachpool.ze_num,eachpool.gr_num, eachpool.rw_num, eachpool.yw_num, eachpool.rz_num, eachpool.yz_num, eachpool.wz_num, eachpool.qt_num) #读出其全部信息self.ticketstack_tree.insert('',self.trains,values=tabulater) #并将这些信息显示出来self.trains+=1 #查到的车数+1 用于记录下一次插到第几行self.ticketstack_tree.pack() #显示表格def setenddays(self): #用于止日可选列表的设定try:startday=self.start_date.get() #尝试能否直接获取起日enddays=[] #能则设定止日列表for i in range(7): #7天内enddays.append(startday) #将起日开始的所有日期装入if startday==self.startdays[59]: #如果碰到了从系统今日开始的第60天,说明已经达到预售期break #退出循环 不再增加startday=next_day(startday) #如果没碰到 就将其变为下一天self.end_date_box["values"]=enddays #将止日的combobox显示列表更新self.end_date_box.set(enddays[0]) #将止日的combobox显示更新except: pass #不能则跳过def morehelp(self):text1="1.输入你出发所在城市和目的地城市的汉语名称,通过“起始时间”和“结束时间”栏确定你需要查票的时间区间,点击“查询”即可\n"         text2="2.推荐首先设置系统使之信任12306根证书,否则本程序将不能完成功能,且您可能会无法访问12306购票网站"tk.messagebox.showinfo("帮助",text1+text2)  #帮助栏def moreabout(self):text1="☺2015-12-10 Programmed by Jason ZHANG\n"text2="   School of Computer Science and Engineering, Beihang Univ."tk.messagebox.showinfo("关于",text1+text2)  #作者栏if __name__=='__main__':User_Interface() #显示查票器

基于Python 3.4 实现的12306查票器相关推荐

  1. 从爬虫构建数据集到CNN模型的验证码识别,一步一步搭建基于Python的PC个人端12306抢票程序

    写在前面:这个程序不是一个人能在短时间内完成的,感谢达纳,王哥的支持帮助.也感谢小平老师,没有压迫,就没有项目. 简介:这是一篇很硬核的Blog, 有一定Python基础的童鞋方能看懂,本程序的主要内 ...

  2. 快过年了,Python实现12306查票以及自动购票....

    嗨害大家好鸭!我是小熊猫~ 明天就是2023年啦~ 还有谁像我小熊猫一样没有回家的? 这次康康能不能12306抢票回家!!! Python实现12306查票以及自动购票 [代码来源]: 青灯教育-自游 ...

  3. python爬虫抢火车票_如何用python写一个简单的12306抢票软件|python 爬火车票 教程...

    python 如果抓取验证码图片 类似12306的登录验证码图片 这个以前做次.最大的麻烦是码的识别算法的识别率太低.12306那种网站登陆错3次就限制你20分钟.所以除非你有33%以上的识别率否则不 ...

  4. 用Python实现一个简单好用的12306查票系统

    春节快到了,又要到一年中紧张刺激的时候了! 买票回家! 今天教大家想做一个简单好用的查票系统! 本次用到环境和模块 环境: Python 3.6 Pycharm 模块 requests pandas ...

  5. 基于python的SQLite数据库增删改查

    与其他数据库管理系统不同,SQLite不是一个客户端/服务器结构的数据库引擎,而是一种嵌入式数据库,他的数据库就是一个文件.SQLite将整个数据库,包括定义.表.索引以及数据本身,作为一个单独的.可 ...

  6. python写一个简单的12306抢票

    引言 每逢过年就到了12306抢票高峰期,自己总想研究一下12306购票的流程,虽然网上已经很多资料,但是总比不过自己的亲身体会,于是便琢磨着写一个抢票软件,本人比较熟悉python,所以软件是用py ...

  7. 二次元看过来!基于 Serverless 的舞萌音游查分器

    前言 本文作者:远哥制造 一.什么是 Serverless Framework Serverless Framework 是业界非常受欢迎的无服务器应用框架,开发者无需关心底层资源即可部署完整可用的 ...

  8. vue+node+mongoDB火车票H5(七)-- nodejs 爬12306查票接口

    菜鸟一枚,业余一直想做个火车票查票的H5,前端页面什么的已经写好了,node+mongoDB 也写了一个车站的接口,但 接下来的爬12306获取车次信息数据一直卡住,网上的爬12306的大部分是pyt ...

  9. [精品毕设]基于Python实现的飞机票销售系统订票系统

    目录 1.项目功能介绍 2.项目资料截图 3.项目运行截图 4.项目资料获取 1.项目功能介绍 <基于Python的飞机票销售系统的设计和实现>该项目采用技术Python的django框架 ...

最新文章

  1. windows中PyCharm的安装和使用
  2. 写给游戏编程自学者的入门指南
  3. 在hive中对日期数据进行处理,毫秒级时间转化为yyyy-MM-dd格式
  4. python框架django文档_Django基础——Django框架介绍及模板语言
  5. u盘插在电脑上灯亮没有反应_u盘插入电脑无反应怎么解决 u盘插入电脑无反应解决方法【步骤介绍】...
  6. PHP CodeBase: 求最近一个周一和上周一的日期
  7. Java中使用foreach带来的一些问题
  8. win10无法执行vbs脚本
  9. day01 继承、抽象类和模板设计模式
  10. WSO2简单使用-rest
  11. dell2900服务器做系统,dell2900如何重装系统
  12. U盘启动盘制作,金士顿2GU盘量产工…
  13. 技术管理者需要认识管理活动的高杠杆率
  14. minecraft崩溃java,je1.7.10,进入世界就崩溃,解决一下
  15. 机械秒表的使用方法_秒表的使用方法!
  16. 数字新基建指南|数据智能如何赋能文旅新增长
  17. 手机图片怎么做成笔记 手机照片笔记怎么制作
  18. Esxi虚拟机备份到本地
  19. 网易中国创业家大赛倒计时,2017年8月报名结束
  20. python制作qq登录界面_Python制作一个仿QQ办公版的图形登录界面

热门文章

  1. java fb app download_fb download app
  2. Transact-SQL 语法元素之数据类型
  3. Python调用百度OCR实现图片文字识别
  4. 用户运营|怎样快速提高留存率?
  5. 使用需求管理工具来提升需求管理和追溯的效率
  6. Python复健练习:爬取58同城房产月租及户型加密字体
  7. DeCAF: A Deep Convolutional Activation Featurefor Generic Visual Recognition阅读报告(1)
  8. 【渝粤题库】陕西师范大学201731教育测量与评价 作业 (专升本、高起本、高起专)
  9. geoserver 发布地图案例
  10. 电脑网页如何安装扩展插件在线观看抖音短视频