美文网首页
Python3火车票信息查询(1)

Python3火车票信息查询(1)

作者: AlexDM | 来源:发表于2017-02-21 23:14 被阅读81次
    售票大厅

    之前写过用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] <from> <to> <date>
    
    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['<from>'])
        to_station = stations.get(arguments['<to>'])
        date = arguments['<date>']
        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()
    

    下面是查询的结果:


    查询结果

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


    程序bug

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

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

    相关文章

      网友评论

          本文标题:Python3火车票信息查询(1)

          本文链接:https://www.haomeiwen.com/subject/utkpwttx.html