用Python实现火车票查询(含票价版)

写在前面:

网上关于用Python3编写火车查询脚本的版本众多,我在前人的基础上编写了自己的这个版本。

我觉得的写的这个版本有以下几个特色:

1,智能引导输入,我一直比较喜欢这种方式。如果直接做成GUI的图形界面,虽然一目了然,知道该点击什么,输入

什么内容,但是显然做GUI比较费时。

所以较之网上的其他版本,要输入一长串字符。设定input引导输入还是不错的选择。

2,支持价格显示,查看网上众多版本,没有一个显示价格的,带着好奇心我编写了显示价格的相关代码并将之实现

(当然期间也遇到了一些问题,这个在后面的代码部分介绍)

3,添加了一些个性化旁白(这个可能并不能成为大家眼中的特色了)

但是还是有一些不足之处:

1,由于学的不是很深入,对class等框架不是特别熟悉,所以代码中没有规范的类,我不知道这样会不会影响代码执行效率,还是希望以后能够更加规范的写代码。

2,添加价格显示之后,执行时间变长,因为价格的信息是通过三次匹配得到的,所查询的列车越多,等待时间越长。

从开始分析网页到最后成功显示相关信息的过程中,还是学到了很多东西。最大的感触就是字典的强大吧,还有就是一些正则匹配技巧,判断关键字是否存在的if语句,将str转化为dic的语句,可以格式化显示数据prettytable模块的用法。

成果展示:

接下来就是一层层剥洋葱的过程:

首先打开12306火车票查询网站,输入查询站名,在审查元素,network中找到相关网址:

打开连接,有新发现:

没错,查询日期内所有列车的信息都在里面(票价除外)

但是不要高兴的太早,现在有两个问题:第一网址怎么匹配,第二得到网址内容之后怎么匹配你需要的信息,比如车

次,时间等。

因为需要匹配两个网址,为了不混淆,我将存储车次等信息的网址成为网址1,存储票价的网址称为网址2.

首先看一下这个网址的组成结构:https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=ADULT&queryDate=2016-09-23&from_station=WHN&to_station=BJP

发现有三个变量:queryDate;from_station;to_station。即时间,出发站,到达站。时间有格式:2016-09-23,中间有

短线,月份也是由两位数构成。站名是对应的字母缩写。所以要找到汉字对应的站名缩写。

有一个网址里面存储了相关信息:https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8955

可以编写关于stations的字典,keys为中文的站名,values为网址中的英文缩写。

'''

@author: yzw

'''

#!/usr/bin/python

# -*- coding:utf-8 -*-

import re

import urllib

from urllib import request

import requests

from pprint import pprint

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

req = urllib.request.Request(url)

r = urllib.request.urlopen(req).read().decode('utf-8')

#print (r)

stations =re.findall(r'([\u4e00-\u9fa5]+)\|([A-Z]+)',r) #匹配中文和对应的英文

stations = dict(stations)

stations = dict(zip( stations.keys(),stations.values()))#将匹配的内容转化为字典

pprint(stations) #以列的形式打印出来

通过以上代码就生成了stations的字典。新建一个stations的py文件,将打印的stations复制到stations中,这样之后调用的时候,直接引用固定的stations.py,而不需要执行生成stations字典的过程。

stations={

'一面坡': 'YPB',

'一面山': 'YST',

'七台河': 'QTB',

'七甸': 'QDM',

'七营': 'QYJ',

'七里河': 'QLD',

'万乐': 'WEB',

'万发屯': 'WFB',

'万宁': 'WNQ',

'万州': 'WYW',

'万州北': 'WZE',

'万年': 'WWG',

'万源': 'WYY',

...}

得到中文对应英文字典,这样一个包含火车相关信息的网址1就匹配出来了。

可以新建一个模块:get_urltrain.py,存储匹配网址1:

from station import stations

import warnings

f1= input('请输入起始城市:\n')

f = stations[f1]

t1= input('请输入目的城市:\n')

t = stations[t1]

d1=input('请输入出发时间: \n')

d=str('2016-')+str(d1) #这里讲年份设置为固定值,可以减少输入操作。

print ('正在查询'+f1+'至'+t1+'的列车,请听听音乐...') #个性旁白

url = 'https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=ADULT&queryDate='+d+'&from_station='+f+'&to_station='+t

warnings.filterwarnings("ignore") #这个网站是有安全警告的,这段代码可以忽略警告

如何解析网址1中的内容:

通过观察发现,每个列车的相关信息都是独立在一个大括号当中,每个大括号里面的内容相当于一个独立的字典,整个网页的内容相当于这些字典构成的列表list。这样可以通过for循环依次提取相关信息了。

每个字典中包含了很多信息,所以我们需要通过网页前端显示的信息和网页源码对比,找到对应的信息,比如:station_train_code就表示表头中的车次…

所以直接在字典中匹配这个英文就会返回车次的信息。其他信息的提取也是如此,这就是字典的方便之处。

还可以添加一个改变显示颜色的模块color_set.py,改变一些双行显示的信息的颜色,比如车站有始发站和到达站,用不同的颜色形成对比:

'''@author: yzw

'''

#!/usr/bin/python

# -*- coding:utf-8 -*-

def colored(color, text):

table = {

'red': '\033[91m',

'green': '\033[92m',

# no color

'nc': '\033[0m'

}

cv = table.get(color)

nc = table.get('nc')

return ''.join([cv, text, nc])

以下是不含票价信息的最后执行模块,代码中都有注释,很容易看懂。

import json

from get_urltrain import d

import urllib

from urllib import request

import requests

from pprint import pprint

from get_urltrain import url

from prettytable import PrettyTable

from color_set import colored

r = requests.get(url, verify=False) #请求网址1的内容

rows = r.json()['data']['datas'] #将内容解析为列表

trains= PrettyTable()

trains.field_names=["车次","车站","时间","历时","商务座","特等座","一等座","二等座","高级软卧","软卧","硬卧 ","软座 ","硬座","无座"]

#设置table的header

num = len(rows) #打印列表的个数

for row in rows : #列表循环

trains.add_row([row['station_train_code'],

'\n'.join([colored('green', row['from_station_name']),

colored('red', row['to_station_name'])]),

'\n'.join([colored('green', row['start_time']), #对于双行示的信息,设置颜色

colored('red', row['arrive_time'])]),

row['lishi'],row['swz_num'],row['tz_num'],

row['zy_num'],row['ze_num'],row['gr_num'],

row['rw_num'],row['yw_num'],row['rz_num'],

row['yz_num'],row['wz_num']])

print ('查询结束,共有 %d 趟列车。'%num ) #列表个数也就是列车个数

print (trains)

运行结果如下图:

如何得到票价的相关信息:

在12306查询之后,初次显示的内容中没有票价的信息,只要当你点击相关座位之后才会在另外一行显示出票价,所以票价和车次的信息不在一个网页当中。

通过上面的分析network方法,找到票价对应的网址2:

https://kyfw.12306.cn/otn/leftTicket/queryTicketPrice?train_no=770000G31005&from_station_no=12&to_station_no=19&seat_types=OM9&train_date=2016-09-23

通过观察发现了里面有了更多的变量:train_no,from_station_no,to_station_no,seat_types,train_date。而这些变量都是网址1中所对应的信息,所以要提出票价的修改信息就要进行二次匹配。

所以在得到网址1的内容之后,通过字典匹配变量的内容,如何匹配出网址2。这个方法和匹配网址1类似。

得到网址2对应的内容:

{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"OT":[],"WZ":"¥519.5","M":"¥827.5","A9":"¥1640.5","9":"16405","O":"¥519.5","train_no":"770000G31005"},"messages":[],"validateMessages":{}}

观察发现我们需要的价格的信息在一个data所对应的大括号当中,但是和之前不同的是整个网址2的内容并不是一个字典,而是一个字符串。这算是我遇到的一个小瓶颈吧。每次直接把它当做字典用就会报错,说str没有key()的属性。然后我就通过百度操作如何将字符串转化为字典的方法,找了很久都没找到。后来看到一个网页上面介绍了一个模块:json和json.loads的方法可以转化。如何用这个方法测试了一下,确实可以。

r_price = urllib.request.urlopen(req).read().decode('utf-8')

r_price = json.loads(r_price)

price = r_price['data']

price = dict(price)

顿时又有了信心,用三次匹配才提取出包含了价格信息的data字典,发现每个车次的价格字典里面,并没有显示所有10个座位类型的价格,还是通过前端和源码的不断对比找到了每个代码代表的车座类型,比如:A1就代表硬座。

因为有些车次只有少许类型,没有的类型的价格信息不会显示。所以直接通过字典将所有类型的车票信息匹配的话,肯定会报错,没有关键字。想了一个办法,就是通过if判断是否有相关车座类型的关键字,如果没有将值设为空,如果有则通过字典匹配后面的价格。这样就有10个if语句了。

A = ('A9' in price.keys()) #判断关键字A9(商务座)是否在字典price中

if A == False :

A =''

else:

A =price['A9']

#TDZ

B = ('P' in price.keys())

if B == False :

B =''

else:

B =price['P']

#YDZ

C = ('M' in price.keys())

if C == False :

C =''

else:

C =price['M']

......

提取出相关的价格信息后就可以将相关信息放在车座类型的后面,一起显示出来。

colored('green',row['swz_num'])+'\n'+A,

colored('green',row['tz_num'])+'\n'+B,

colored('green',row['zy_num'])+'\n'+C, #后面的A,B,C就是价格的信息。

最后运行的结果如开始图片所示。

虽然功能实现了,但是运行速度很慢,我觉得就是因为三次匹配,而且后两次的匹配都在row in rows循环中,每一次循环都有执行一次匹配网址2,匹配data,执行10个关键字是否存在的if语句。所以到最后print(table)的过程要漫长很多。

最后一个令我纠结的问题是,我用prettytable打印出来的包含中文的表格并不是很整齐,虽然我查到了prettytable的英文介绍只支持ascii字符的排列。但是我看到网上的图片都可以将中文对齐,最后在第二天早上我换用了pycharm软件运行,结果显示正常。

我整理了两个版本,一个包含价格的,一个不包含价格的。

都在我的github中,欢迎批评指正。最后附上github中的完整代码

python判断火车票座位_用Python实现火车票查询(含票价版)相关推荐

  1. python判断回文_用python判断回文数

    信息举报 时间:2020-11-23 本页为您甄选多篇描写用python判断回文数,用python判断回文数精选,用python判断回文数大全,有议论,叙事 ,想象等形式.文章字数有400字.600字 ...

  2. python123判断火车票座位_【Python】实现12306余票监控

    首先我们打开12306余票查询窗口 上图红色框的地方,就是表示列车有无车票的地方,我们需要根据这里边的数据来判断. 这里边有一些需要注意的就是,里边表示有票的有字符串"有"和数字& ...

  3. python判断nan格式_关于Python中Inf与Nan的判断问题详解

    大家都知道 在Python 中可以用如下方式表示正负无穷: float("inf") # 正无穷 float("-inf") # 负无穷 利用 inf(infi ...

  4. python判断能否组成三角形_【python+任意输入3个数+判断能否组成三角形】 - #1

    2015年07月18 - 任意输入3个数,判断能否组成三角形,并输出三角形为等边/等腰/直角/普通三角形. 三角形:两边之和大于第三边 直角三角形:勾股定理 #!/usr/bin/python # - ...

  5. python 判断数字连续_关于python:检测列表中的连续整数

    本问题已经有最佳答案,请猛点这里访问. 我有一个包含这样的数据的列表: [1, 2, 3, 4, 7, 8, 10, 11, 12, 13, 14] 我想打印出连续整数的范围: 1-4, 7-8, 1 ...

  6. python判断是否闰年_【python实例】判断输入年份是否是闰年

    原博文 2019-12-24 10:24 − 1 ''' 2 求给定的年份,是否是闰年. 3 满足以下两点中任意一点就是闰年 4 A:能被4整除,但是不能被100整出. 5 B:能被400整除. 6 ...

  7. python判断字母大小写_用python如何判断字符的大小写

    Python提供了isupper(),islower(),istitle()方法用来判断字符串的大小写. 1.isupper()方法 Python isupper() 方法检测字符串中所有的字母是否都 ...

  8. python判断复数浮点数_荐Python数据类型(整数,浮点数,复数,字符串,format()用法)(超详细笔记)...

    一.整数类型 1.与数学中的整数概念一致,没有取值范围限制. 2.整数类型共有4种进制表示:十进制.二进制.八进制. 十六进制,默认十进制,其他进制需增加引导符(不区分 大小写) 二进制 0b或0B, ...

  9. 用Python实现火车票查询(含票价版)

    用Python实现火车票查询(含票价版) 写在前面: 网上关于用Python3编写火车查询脚本的版本众多,我在前人的基础上编写了自己的这个版本. 我觉得的写的这个版本有以下几个特色: 1,智能引导输入 ...

  10. python判断正确错误_第16天:Python 错误和异常

    by 闲欢 作为 Python 初学者,在刚学习 Python 编程时,经常会看到一些报错信息,这些报错信息就是我们接下来要讲的错误和异常. 我们在执行程序语句的时候,经常会看到命令行输出报错信息,例 ...

最新文章

  1. 卷积后feature map尺寸计算公式
  2. stp和vrrp的配置命令
  3. 【es6】es6学习笔记
  4. 【SQL】数据库的SQL查询,涉及多个数据库
  5. java面板如何设置大小_java面板调整大小问题
  6. python中的out of loop_TclError: out of stack space (infinite loop?)
  7. 3月6日云栖精选夜读:如何实现32.5万笔/秒的交易峰值?阿里交易系统TMF2.0技术揭秘...
  8. oracle修改某表中的顺序,oracle 数据库 , 表中字段顺序修改
  9. 在SharePoint环境中更换密码
  10. 《剑指Offer》24:反转链表
  11. 使用Box2D制作AS3游戏——2.1a版本——Hello World Box2D .
  12. HCNP学习笔记:OSPF报头及各种报文格式
  13. 小程序input获得焦点触发_小程序学习(三)
  14. 设某链表中最常用的操作是在链表的尾部_面试官:“双向链表”都不会,谁给你跳槽的勇气?...
  15. 测试TF card 的读写速度
  16. Java Web开发技术应用——监听器
  17. iOS 16横竖屏切换适配
  18. .NetCore基于SignalR、Reids实现客服WebIM系统
  19. 神州信息“六合上甲”金融实践,助力金融机构打造数据底座
  20. CentOS 路由设置

热门文章

  1. 陕西副市长猝死,其“豪宅”爆光震惊国人
  2. 变量undefined详解
  3. Unity3D 2D设计小游戏移动幻影的实现
  4. 【业务办理】广州市户口市内迁移流程
  5. python中系统找不到指定文件怎么办_python 系统找不到指定的文件
  6. atom芯片和服务器芯片冲突,Intel Atom处理器惊天BUG撂倒大波队友:迟来的修复!...
  7. 易到完成股权变更 乐视仍未完全退出中信系入局
  8. 【沃顿商学院学习笔记】商业基础——Financing:05 名义利率和实际利率 APR EAR
  9. ORA-20003: ORU-10036: object XXX is invalid and cannot be described
  10. 【图解CDD】利用CANdelaStudio编辑诊断描述CDD文件带你入门到精通