Python3火车票信息查询(1)

Python3火车票信息查询(1)_第1张图片
售票大厅

之前写过用Python查询火车票的小程序,后来不可以用了,是由于12306网站改版,导致一些参数发生了变化,今天又做了一下,调试成功了,现在把修改的过程记录一下。

这次主要的代码是使用了实验楼的一个教程案例,对其中的部分参数进行了修改:
1、获取站名的URL地址中的station_version调整为最新的1.8997
2、余票查询的地址也变了,具体可以看下面的代码
3、请求返还的数据结构也发生一些变化,数据放在json字符串的data里面,并且是一个list,list中的元素是每趟火车的信息(这是字典结构),而对我们有用的火车信息放在queryLeftNewDTO里面

在这个小程序中需要使用的库有:
requests、docopt、colorama、prettytable

对于具体的获取地址、参数等,在此不赘述,可以参考实验楼的教程,或者在网上搜一下。

#parse_stations.py
# 通过以下语句生成车站代码:python3 parse_stations.py > stations.py
# 获取车站的编码
import re
import requests
from pprint import pprint 
# pprint 提供了打印出任何Python数据结构类和方法
# pprint.pprint(object,stream=None,indent=4, width=80, depth=None) 
# 输出格式的对象字符串到指定的stream,最后以换行符结束

url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8997'
response = requests.get(url, verify=False)
stations = re.findall(u'([\u4e00-\u9fa5]+)\|([A-Z]+)', response.text)
pprint(dict(stations), indent=4)
# coding: utf-8
# tickets.py

"""命令行火车票查看器

Usage:
    tickets [-dgktz]   

Options:
    -h, --help 查看帮助
    -d         动车
    -g         高铁
    -k         快速
    -t         特快
    -z         直达

Examples:
    tickets 上海 安阳 2016-10-10
    tickets -dg 上海 安阳 2016-10-10
"""

import requests
from docopt import docopt
from prettytable import PrettyTable
from colorama import init, Fore
from stations import stations

init() # init(autoreset=True) 添加了这个参数之后,对于输出的颜色就不需要每次都Fore.RESET
class TrainsCollection:
    header = '车次 车站 时间 历时 一等 二等 软卧 硬卧 硬座 无座'.split()
    def __init__(self, available_trains, options):
        """查询到的火车班次集合
        :param available_trains: 一个列表, 包含可获得的火车班次, 每个
                                 火车班次是一个字典
        :param options: 查询的选项, 如高铁, 动车, etc...
        """
        self.available_trains = available_trains
        self.options = options

    def _get_duration(self, raw_train):
        duration = raw_train.get('lishi').replace(':', '小时') + '分'
        if duration.startswith('00'): # 如果字符串是以00开头的则返回TRUE
            return duration[4:]
        if duration.startswith('0'):
            return duration[1:]
        return duration

    @property
    def trains(self):
        for raw_train in self.available_trains:
            raw_train = raw_train.get('queryLeftNewDTO') 
            if raw_train is not None and raw_train != '':
                train_no = raw_train['station_train_code']
                initial = train_no[0].lower()
                if not self.options or initial in self.options:
                    train = [
                    train_no,        
                    '\n'.join([Fore.GREEN + raw_train['from_station_name'] + Fore.RESET,
                               Fore.RED + raw_train['to_station_name'] + Fore.RESET]),
                    '\n'.join([Fore.GREEN + raw_train['start_time'] + Fore.RESET,
                               Fore.RED + raw_train['arrive_time'] + Fore.RESET]),
                    self._get_duration(raw_train),
                    raw_train['zy_num'],
                    raw_train['ze_num'],
                    raw_train['rw_num'],
                    raw_train['yw_num'],
                    raw_train['yz_num'],
                    raw_train['wz_num'],
                ]
                    yield train

    def pretty_print(self):
        pt = PrettyTable()
        pt._set_field_names(self.header)
        for train in self.trains:
            pt.add_row(train)
        print(pt)


def cli():
    """Command-line interface"""
    arguments = docopt(__doc__)
    from_station = stations.get(arguments[''])
    to_station = stations.get(arguments[''])
    date = arguments['']
    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
           )
    options = ''.join([
        key for key, value in arguments.items() if value is True
    ])
    r = requests.get(url, verify=False)
    available_trains = r.json()['data']
    TrainsCollection(available_trains, options).pretty_print()

if __name__ == '__main__':
    cli()

下面是查询的结果:


Python3火车票信息查询(1)_第2张图片
查询结果

在后续的调试过程还发现了一个有意思的地方,就是当我修改目的地为昆山的时候,发现有历时近100个小时,这应该是程序的bug,没有对这个预售的状态进行判断,在返回的数据结果中buttonTextInfo的值一般是‘预售’,我估计可以根据这个值判断一下。


程序bug

另外对于结果的排序也没有做,我觉得可以再完善一下,后面有时间了可以再补充一下。
这个方法不是很完美:
print(pt.get_string(sortby='历时',reversersort=False))

参考资料:
https://www.shiyanlou.com/courses/623

你可能感兴趣的:(Python3火车票信息查询(1))