最近在实验楼学到了一个使用的小项目,用python写一个优雅的火车票查看器,不用登陆12306网站,只要在命令行输入一行命令就可以获得你想要的火车票信息。
欣赏效果图
打开12306查询8月13日从郑州到杭州的快速火车的车票
下面用命令行版火车票查看器查询同样的信息:
有没有感觉到界面很优雅,排版很整齐,还有颜色的区分,虽然不如12306的页面好看,也独具一番特色。
环境:
- 操作系统 : ubuntu 16.04
- python环境:python 3.5.2
安装python模块
- requests模块 : 使用Python访问HTTP资源的必备库。
- docopt模块 : 命令行参数解析工具。
- prettytable模块 : 格式化信息打印工具,能让你像 MySQL 那样打印数据。
- colorama模块,命令行着色工具
这些模块都可以使用下面的语句安装:
sudo pip3 install [模块名]
设计
1.接口设计
我们要查询票务信息,需要输入出发站,终点站,日期,火车的类型。
从12306官网的余票查询可以看到火车的类型有五种选项:
- -g 高铁
- -d 动车
- -z 直达
- -t 特快
- -k 快速
注意:查询的时候选项可以组合,所以我们的接口为:
python3 tickets.py 火车类型 出发站 到达站 日期
2.解析参数
解析参数的目的是为了将我们的命令行中的火车类型,出发站,到达站,日期作为已知量,更方便在后面获取url中填充,使缺参url加上已知量组成我们要查询信息的url。不懂的话可以看下这个小例子:http://blog.csdn.net/xunalove/article/details/77040819#t7
3.获取数据和解析数据:
要获取信息需要构造url—>发送请求—>获取数据:
3.1怎么构造url
打开12306官网,查看一次余票,按F12打开开发者工具,选择network里面可以看到两项,第二项就是我们请求返回的url。
双击会跳转到如下界面:
可以看到result是一个字典,字典里面每一个列表就是一行票务信息。这个url就是需要我们构造的url,
先看看url的构成:
https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-08-13&leftTicketDTO.from_station=ZZF&leftTicketDTO.to_station=HZH&purpose_codes=ADULT
里面有四个参数:
- train_date=2017-08-13
- from_station=ZZF
- to_station=HZH
- purpose_codes=ADULT
我们发现出发站和目的站并不是汉字而是大写的字符代号。
在网页里面有一个链接带有station-vesion里面包含所有车站的代码。
打开连接之后
我们需要将这两项提取出来(下面是一种通过url请求的提取方法)构造一个正则表达式模式串,过滤出来我们想要的信息:汉字+字符代号(中间用“|”隔开要进行转义),之后转换为字典类型,分开保存一个为汉字,一个为对应的字符代码,这样在命令行输入汉字的时候根据下面找到对应的汉字的下标找到对应的字符代号。
# !usr/bin/env/python3.5.2
# -*- coding:utf-8 -*-
import requests
import re #正则表达式的包
from pprint import pprint
def main():
url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9023'
r = requests.get(url, verify = False)
#verify = False 禁止证书验证
#print (r.text)
pattern = u'([\u4e00-\u9fa5]+)\|([A-Z]+)'
#print (re.findall(pattern,r.text))
result = dict(re.findall(pattern, r.text))
#print (dict(result))
print (result.keys())
print (result.values())
if __name__ == "__main__":
main()
将运行结果重定向到stations.py里面
python3 parse_station.py >> stations.py
有了字符代号就可以构造url了
url = ( 'https://kyfw.12306.cn/otn/leftTicket/query?'
'leftTicketDTO.train_date={}&'
'leftTicketDTO.from_station={}&'
'leftTicketDTO.to_station={}&'
'purpose_codes=ADULT').format(date, from_station, to_station)
r = requests.get(url, verify=False)
raw_trains = r.json()['data']['result']
此时我们发送请求得到的数据就如同上图得到的数据,十分混乱,为了得到我们的目标数据,需要对获取的数据进行解析,要解析首先需要知道12306网页是怎么解析信息的,打开开发者工具里面的sources
可是看到它是通过for循环实现的,下面还有个参数的信息
这部分代码(同时通过prettytable模块将数据格式化,通过colorama模块代码着色部分):
pt = PrettyTable()
pt._set_field_names('车次 车站 时间 历时 商务座 一等座 二等座 高级软卧 软卧 动卧 硬卧 软座 硬座 无座'.split())
for raw_train in raw_trains:
data_list = raw_train.split('|')
train_no = (data_list[3]) #车次
initial = train_no[0].lower()
if initial in options or not options:
from_station_code= (data_list[6])
to_station_code= (data_list[7])
from_station_name =''
to_station_name= ''
start_time = data_list[8]
arrive_time = data_list[9]
time_duration = data_list[10]
normal_seat = data_list[32] or '--'
first_class_seat = data_list[31] or '--'
second_class_seat = data_list[30] or '--'
ssoft_sleep = data_list[21] or '--'
soft_sleep = data_list[23] or '--' #为空显示--
move_sleep = data_list[33] or '--'
hard_sleep = data_list[28] or '--'
soft_seat = data_list[24 ] or '--'
hard_seat = data_list[29] or '--'
no_seat = data_list[26] or '--'
#显示
pt.add_row([
''.join([Fore.RED + train_no + Fore.RESET]),
'\n'.join([Fore.GREEN + stations.get_name(from_station_code) + Fore.RESET,Fore.YELLOW + stations.get_name(to_station_code)+ Fore.RESET ]),
'\n'.join([Fore.GREEN + start_time + Fore.RESET, Fore.YELLOW + arrive_time + Fore.RESET]),
time_duration,
normal_seat,
first_class_seat,
second_class_seat,
ssoft_sleep,
soft_sleep,
move_sleep,
hard_sleep,
soft_seat,
hard_seat,
no_seat
])
到这里一个简单实用的实时火车票查看器就完成了。
完整代码:https://github.com/xuna123/Python_pro
遇到的问题:
输入命令行,运行结果上面一直输出一段警告性的文字?
原因:因为我们使用了”verify = False”是浏览器访问2306时禁止验证。
解决方法:在程序的首部添加如下代码,忽略无害的警告:
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
总结:
从这个小项目中学到了很多东西,python的参数解析库,格式化库,还有命令行着色小工具,原来命令行也可以优雅的显示。最重要的是学习了项目的思路
如何从开发者工具里找到我们想要的url,怎么根据页面js的显示来设计程序的显示方式,这是我总结的项目的流程:
解析参数—构建url—获取信息—解析信息成想要的信息—设计样式。
参考资料:
https://stackoverflow.com/questions/27981545/suppress-insecurerequestwarning-unverified-https-request-is-being-made-in-pytho
https://www.bilibili.com/video/av12380578/