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

2023-11-19

   用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")         #这个网站是有安全警告的,这段代码可以忽略警告</span></span>

 

  • 如何解析网址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_nofrom_station_noto_station_noseat_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)</span>

顿时又有了信心,用三次匹配才提取出包含了价格信息的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中的完整代码

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用Python实现火车票查询(含票价版) 的相关文章

  • [从零开始学DeepFaceLab-5]: 使用-命令行八大操作步骤-第2步:从源视频中提取图片

    目录 总体流程 步骤2 从源视频中提取图片 2 0 源视频文件和大小的选择 2 1 命令 2 extract images from video data src bat 必选
  • Qt

    我的RFID程序中 Widget继承自QWidget 在Widget h中 public定义了数据成员 QTableWidget cardtableWidget 在Widget cpp的Widget构造函数中 初始化了QTableWidge
  • 简明YAML教程

    前言 yaml是一种用来描述配置的语言 其可读性和简洁性较json更胜一筹 用yml写成的配置文件 以 yml结尾 YAML的基本语法规则 大小写敏感 使用缩进表示层级关系 缩进是使用空格 不允许使用tab 缩进对空格数目不敏感 相同层级需
  • 浏览器播放rtsp视频流:3、rtsp转webrtc播放

    浏览器播放rtsp视频流 3 rtsp转webrtc播放 文章目录 浏览器播放rtsp视频流 3 rtsp转webrtc播放 1 前言 2 rtsp转webRTC 3 初步测试结果 4 结合我们之前的onvif gSoap cgo的方案做修
  • z-index 与 元素的层叠顺序

    z index 属性设置元素的堆叠顺序 拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的元素的前面 注释 元素可拥有负的 z index 属性值 注释 Z index 仅能在定位元素上奏效 例如 position absolute 说明 该属
  • Basic Level 1018 锤子剪刀布 (20分)

    题目 大家应该都会玩 锤子剪刀布 的游戏 两人同时给出手势 胜负规则如图所示 现给出两人的交锋记录 请统计双方的胜 平 负次数 并且给出双方分别出什么手势的胜算最大 输入格式 输入第 1 行给出正整数 N 1 0 5
  • c++ 学习之set和multiset区别

    区别 set不允许有重复的值 multiset可以有重复的值 代码示例 include
  • 技术干货分享,万字长文深度解读机器翻译

    编者按 在 机器翻译是如何炼成的 上 的文章中 我们回顾了机器翻译的发展史 在本篇文章中 我们将分享机器翻译系统的理论算法和技术实践 讲解神经机器翻译具体是如何炼成的 读完本文 您将了解 神经机器翻译模型如何进化并发展成令NLP研究者万众瞩
  • CF、SF、OF、ZF标志位

    没学汇编 这种题我真是做一道错一道 OF overflow flag 溢出标志位 溢出标志位 OF 1 表示带符号整数运算时结果发生溢出 对于无符号整数运算 OF没有意义 对于有符号数的溢出判断方式有 1 采用一位符号位 思想为 或 则为溢
  • 利用dbnet分割条形码与文字(代码+模型)+知识蒸馏+tensorrt推理+利用pyzbar和zxing进行条形码解析

    一 DBnet 1 代码链接 分割条形码与文字代码 github链接 GitHub zonghaofan dbnet torch you can use dbnet to detect word or bar code Knowledge
  • CocosCreator之KUOKUO带你做小小赛车-摄像机跟随

    本次引擎2 0 5 编辑工具VSCode 目标 小小赛车 先亮素材 很简单 就两个 爱给网中的赛道 以及一个小车 好了 让我们新建工程然后把赛道放进去 调整方向与大小 然后把小车拖上去 这样 我是把赛道放大了2倍 旋转了90度 拖一拖位置
  • onclick传参使用function()

    对于有需要传参的按钮 需要按照以下的方式进行 直接上代码
  • 9、Linux(Ubuntu 18)安装Redis以及C操作Redis

    扩展知识 头文件搜索 Linux中库的头文件 首先include有两种写法 一种是 include 另一种是 include xxx 这两种写法的区别是 include xxx 会首先在当前目录下搜索头文件 不递归 如果找不到的话再去系统目
  • 3分钟玩转:ES6 模块化

    ES6 模块 ES6 使用 export 和 import 导出和导入模块 导出模块 一个模块就是一个独立的 JS 文件 该文件内的变量外部无法获取 若希望能让外部获取模块内的变量 则要用 export 关键字暴露变量 分别暴露 命名行内导
  • Windows11右键菜单太烦人,简单几步即可恢复旧版完整菜单

    Windows 11已经推出一段时间了 相比Windows 10 界面确实美观了不少 同时也有很多新的设计 但是并不是每个人都能很快适应这种新设计 被广泛吐槽的一点就是右键菜单的改变 增加了显示更多选项 原来的很多右键选项被隐藏起来了 原本
  • tkinter 的界面美化库:ttkbootstrap 使用教程

    嗨害大家好鸭 我是芝士 tkbootstrap 是一个基于 tkinter 的界面美化库 使用这个工具可以开发出类似前端 bootstrap 风格的 kinter 桌面程序 如果会 tkinter 学习起来就会非常简单 如果不会的话只要先花
  • opencv python contours结构

    opencv python contours结构 经常需要构造 如果没记住内部具体结构 需要到网上处找 且找不到 就要自己findcontours然后打印出来 比较麻烦 contours的结构 比如一个box有xmin ymin xmax

随机推荐

  • 今天发现一个好网站 http://www.phpv.net/

    该网站的空间速度快 资料丰富 容易搜索 更新快 爽
  • 运维之道

    方法一 rc local 1 由于在centos7中 etc rc d rc local的权限被降低了 所以需要赋予其可执行权 chmod x etc rc d rc local 2 赋予脚本可执行权限 假设 opt script auto
  • pytorch训练error

    问题一 在pytorch上训练分割模型时 出现cuda runtime error 59 device side assert triggered at xxx 解决办法 通过CUDA LAUNCH BLOCKING 1 python3 m
  • python----小数点精度控制round()

    python版本也会影响结果 python2把x四舍五入为远离0的最近倍数 如round 0 5 1 round 0 5 1 python3则会把x四舍五入为最近的偶数倍数 如round 0 5 0 round 1 5 2 0 round
  • 查看解决inode使用率100%的问题

    今天登录后端服务器查看 发现程序报错日志中存在磁盘空间不足的情况 df h后发现磁盘空间充足 df ih发现 app分区inode使用率100 开始查找原因 进到 app 下 然后 for i in do echo i find i wc
  • hashMap常见的问题解答

    1 HashMap的数据结构 hashmap采取数组 链表的数据结构 在遇到哈希冲突的时候采用链表结构来解决哈希冲突 jdk1 8后分成了两种情况 bucket中元素个数大于8的时候 自动转换为红黑树的结构 目的是因为链表的查询速度比较慢
  • vue+element table 合并列

    vue element table 合并列
  • 【TCP/IP详解 卷一:协议】TCP的小结

    前言 TCP学习的综述 在学习TCP IP协议的大头 TCP协议 的过程中 遇到了很多机制和知识点 详解中更是用了足足8章的内容介绍它 TCP协议作为 应用层 和 网络层 中间的 传输层协议 既要为下面的网络层协议保证连接的可靠性 IP协议
  • 通过Jib将Springboot应用通过Docker部署

    一 安装Docker 1 更新Yum包 yum update 2 卸载旧版本 如果安装过旧版本的话 1 删除软件包 yum remove y docker docker client docker client latest docker
  • 【Espruino】NO.14 温湿度传感器DHT11

    http blog csdn net qwert1213131 article details 35828873 本文属于个人理解 能力有限 纰漏在所难免 还望指正 小鱼有点电
  • 环境变量是如何生效的——以Linux操作系统为例

    什么是环境变量 从我们学习Java开始 就经常接触一个东西 PATH 也叫环境变量 环境变量是操作系统提供给应用程序访问的简单 key value字符串 windows linux mac都有同样的概念 环境变量的作用 当我们拥有一个可执行
  • Git版本回退并强制推送到远端

    Git版本回退并强制推送到远端 本文参考廖雪峰的Git教程 前言 本文章解决问题的前提是本人不小心修改了本地代码仓库的最外层目录权限 不知道原权限是什么 导致本地git提示几十个文件被修改过 实际内容并未修改 可能是目录权限改变被git识别
  • C++ - 继承 一些 细节 - 组合 和 继承的区别

    前言 本篇博客基于 C 继承 chihiro1122的博客 CSDN博客 之上列出一些例子 如果有需要请看以上博客 继承的例子 例1 上述例子应该选择 C 首先不用说 p3肯定是指向 d 对象的开头的 p1 也是指向 d 对象的开头的 不同
  • 机器学习即服务:关于情感分析的10个应用场景和4个服务

    情感分析是什么 用户生成内容的爆炸式增长和档案材料的数字化创造了大量的数据集 其中包含了许多人对几乎每一个主题发表的观点 在某些情况下 该数据的生成是通过用户界面构造的 例如 在电子商务网站上处理客户评论相对容易 因为用户需要在产品评论的文
  • C++模板基础(六)

    类模板与成员函数模板 使用 template 关键字引入模板 template class B 类模板的声明与定义 翻译单元的一处定义原则 template
  • html5页面刷新时显示新数据,用ajax使网页不刷新就可以显示新数据

    用AjaxPro的 1 在引用中添加引用AjaxPro dll 我用的是这个 支持asp net 1 1 和asp net 2 0 2 web config中建立HttpHandler 3 新建一个类Demo 这个类里面提供了查询数据库和输
  • 怎么在html中复制粘贴图片,如何复制其他网页上的文章和图片

    首先 我们并不建议直接复制别人的内容 以免侵权 但是不排除在某些情况下 需要将其他网页上的内容完整的复制到你的网站中 比如 将自己的微博文章复制到自己的网站 大部分人都知道这个简单的方法 先选中目标文字按CTRL C快捷键 然后再网站后台文
  • AngularJS学习之全局API(应用程序编程接口)

    1 AngularJS全局API用于执行常见任务的Javascript函数集合 比较对象 迭代对象 转换对象 2 全局API函数使用angularJS对象进行访问 以下是通用API函数 angular lowercase 转换字符串为小写
  • Unity用Vuforia做AR实现脱卡效果

    有时在识别目标丢失后我们仍希望虚拟物体能够出现在摄像机前 或者到一个特定的位置 我们能对其进行操作 这就是脱卡功能 自带的脱卡功能应该是ExtendedTracking 允许模型在识别图丢失的时候还存在 位置不变 在丢失的时候的位置 这样也
  • 用Python实现火车票查询(含票价版)

    用Python实现火车票查询 含票价版 写在前面 网上关于用Python3编写火车查询脚本的版本众多 我在前人的基础上编写了自己的这个版本 我觉得的写的这个版本有以下几个特色 1 智能引导输入 我一直比较喜欢这种方式 如果直接做成GUI的图