火车票查询系统(一)——利用python实现监控12306余票查询

最近春运高峰,本人从12月开始买过年回家的火车票,然鹅~现实总是那么残酷,放票时间记不住,要么就是不能实时盯着刷票,外面的软件也不知道靠不靠谱,反正是没买到票,所以一怒之下,自己做一个12306余票查询系统,可以实现以下一些功能:

1. 设置目标位置进行查询余票
2. 通过设定条件监控到余票后进行12306账号登录
3. 12306登录及图片识别(自动登录功能)
4. 自动下单功能
5. 邮件通知付款功能

ok,整个博客内容包括以上代码实现,我们按照惯例,循序渐进,先讲第一篇【实现12306余票查询】

-----------------------------------------------------我是分割线、

第一步:查看12306查询接口
火车票查询系统(一)——利用python实现监控12306余票查询_第1张图片
通过截图我们可以看到,查询接口是:

https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date=2020-01-16&leftTicketDTO.from_station=SZQ&leftTicketDTO.to_station=SHH&purpose_codes=ADULT

https://kyfw.12306.cn/otn/leftTicket/queryZ + data + from_station + to_station + purpose_codes
这样一个连接拼接而成,那我们要做的就是按照他这种格式,传入参数进去即可。

第一步:定义类并对接口参数进行设置和组装

class LeftTicketDTO:
    """
    查询余票参数信息
    """
    def __init__(self, train_date=datetime.date.today().strftime('%Y-%m-%d'), from_station='SZQ',
                 to_station='GZQ', purpose_codes='ADULT'):
        self.train_date = train_date
        self.from_station = from_station
        self.to_station = to_station
        self.purpose_codes = purpose_codes

    def encode_url_para(self):
        paras = ['train_date=' + self.train_date,
                 'from_station=' + self.from_station,
                 'to_station=' + self.to_station]
        p = '&'.join(['leftTicketDTO.' + v for v in paras])
        return p + '&purpose_codes=' + self.purpose_codes

第二步:我们把所需要的车次信息捞出来,进行初始化

class Train:
    """
    车次信息
    """
    def __init__(self, item):
        """
        使用列表顺序初始化火车信息
        :param item: 12306火车信息列表
        :return:
        """
        # self.field_0 = item[0]
        # 状态
        self.status = item[1]
        # 车票号
        self.train_no = item[2]
        # 车次
        self.station_train_code = item[3]
        # 起始站代号
        self.start_station_code = item[4]
        # 终点站代号
        self.end_station_code = item[5]
        # 出发站代号
        self.from_station_code = item[6]
        # 到达站代号
        self.to_station_code = item[7]
        # 出发时间
        self.start_time = item[8]
        # 到达时间
        self.arrive_time = item[9]
        # 运行时长
        self.run_time = item[10]
        # 是否可买
        self.can_buy = item[11]
        self.yp_info = item[12]
        # 出发日期
        self.start_train_date = item[13]
        self.train_seat_feature = item[14]
        self.location_code = item[15]
        self.from_station_no = item[16]
        self.to_station_no = item[17]
        self.is_support_card = item[18]
        self.controlled_train_flag = item[19]
        self.gg_num = item[20]
        self.gr_num = item[21]
        self.qt_num = item[22]
        # 软卧
        self.rw_num = item[23]
        # 软座
        self.rz_num = item[24]
        self.tz_num = item[25]
        # 无座
        self.wz_num = item[26]
        self.yb_num = item[27]
        # 硬卧
        self.yw_num = item[28]
        self.yz_num = item[29]
        # 二等座
        self.edz_num = item[30]
        # 一等座
        self.ydz_num = item[31]
        # 商务特等座
        self.swz_num = item[32]
        # 动卧
        self.dw_num = item[33]
        self.yp_ex = item[34]
        self.seat_types = item[35]
        self.exchange_train_flag = item[36]

    @property
    def key_info(self):
        """
        获取车次的关键信息
        :return:
        """
        return {
            '状态': self.status,
            '车次': self.station_train_code,
            '起始站代号': self.start_station_code,
            '终点站代号': self.end_station_code,
            '出发站代号': self.from_station_code,
            '到达站代号': self.to_station_code,
            '出发时间': self.start_time,
            '到达时间': self.arrive_time,
            '运行时长': self.run_time,
            '是否可买': self.can_buy,
            '出发日期': self.start_train_date,
            '软卧': self.rw_num,
            '软座': self.rz_num,
            '无座': self.wz_num,
            '硬卧': self.yw_num,
            '二等座': self.edz_num,
            '一等座': self.ydz_num,
            '商务特等座': self.swz_num,
            '动卧': self.dw_num
        }


第三步:请求12306地址,进行查询

def get_trains(dto):
    """
    获取余票信息
    :param dto:
    :return:
    """
    # 余票查询链接地址可能会变,其中结尾处的Z可能会被替换为[A-Z],注意!!!
    url = 'https://kyfw.12306.cn/otn/leftTicket/queryZ'
    url = url + '?' + dto.encode_url_para()
    # 请求头
    hds = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/74.0.3729.157 Safari/537.36',
        'Cookie': 'JSESSIONID=F8909A46584B13B74DE18535CC9DFE4;'
    }
    try:
        trains = []
        print('正在查询余票信息:', url)
        # 访问请求链接
        rsp = requests.get(url, headers=hds)
        text = json.loads(rsp.content.decode())

        # 检查返回码
        if text['httpstatus'] != 200:
            print('获取余票信息异常,', text)
            return

        # 获取想要的数据
        result = text['data']['result']
        print('共获取到{0}趟车次信息:'.format(len(result)))
        for v in result:
            item = [f for f in v.split('|')]
            t = Train(item)
            trains.append(t)
    except Exception as e:
        print('获取数据异常, {0} -> {1}'.format(Exception, e))
    return trains

第四步:传参并进行查询

备注:如何看站点唯一标识,可以通过以下接口看:https://kyfw.12306.cn/otn/resources/js/framework/station_name.js
或者直接在12306页面请求,
火车票查询系统(一)——利用python实现监控12306余票查询_第2张图片

def test():
    """
    测试代码
    :return:
    """
    import prettytable

    # 获取票信息
    para = LeftTicketDTO(from_station='IOQ', to_station='SNH', train_date='2020-01-15')
    trains = get_trains(para)
    if not trains:
        print('未找到符合条件的车次信息')
        return

    # 组装信息
    tab = prettytable.PrettyTable()
    # 标题头
    tab.field_names = trains[0].key_info.keys()
    # 组装车次信息
    for t in trains:
        tab.add_row(t.key_info.values())
    # 表格显示车次信息
    print(tab)

完整代码如下:

# -*- coding: utf-8 -*-
import json
import requests
import datetime


class Train:
    """
    车次信息
    """
    def __init__(self, item):
        """
        使用列表顺序初始化火车信息
        :param item: 12306火车信息列表
        :return:
        """
        # self.field_0 = item[0]
        # 状态
        self.status = item[1]
        # 车票号
        self.train_no = item[2]
        # 车次
        self.station_train_code = item[3]
        # 起始站代号
        self.start_station_code = item[4]
        # 终点站代号
        self.end_station_code = item[5]
        # 出发站代号
        self.from_station_code = item[6]
        # 到达站代号
        self.to_station_code = item[7]
        # 出发时间
        self.start_time = item[8]
        # 到达时间
        self.arrive_time = item[9]
        # 运行时长
        self.run_time = item[10]
        # 是否可买
        self.can_buy = item[11]
        self.yp_info = item[12]
        # 出发日期
        self.start_train_date = item[13]
        self.train_seat_feature = item[14]
        self.location_code = item[15]
        self.from_station_no = item[16]
        self.to_station_no = item[17]
        self.is_support_card = item[18]
        self.controlled_train_flag = item[19]
        self.gg_num = item[20]
        self.gr_num = item[21]
        self.qt_num = item[22]
        # 软卧
        self.rw_num = item[23]
        # 软座
        self.rz_num = item[24]
        self.tz_num = item[25]
        # 无座
        self.wz_num = item[26]
        self.yb_num = item[27]
        # 硬卧
        self.yw_num = item[28]
        self.yz_num = item[29]
        # 二等座
        self.edz_num = item[30]
        # 一等座
        self.ydz_num = item[31]
        # 商务特等座
        self.swz_num = item[32]
        # 动卧
        self.dw_num = item[33]
        self.yp_ex = item[34]
        self.seat_types = item[35]
        self.exchange_train_flag = item[36]

    @property
    def key_info(self):
        """
        获取车次的关键信息
        :return:
        """
        return {
            '状态': self.status,
            '车次': self.station_train_code,
            '起始站代号': self.start_station_code,
            '终点站代号': self.end_station_code,
            '出发站代号': self.from_station_code,
            '到达站代号': self.to_station_code,
            '出发时间': self.start_time,
            '到达时间': self.arrive_time,
            '运行时长': self.run_time,
            '是否可买': self.can_buy,
            '出发日期': self.start_train_date,
            '软卧': self.rw_num,
            '软座': self.rz_num,
            '无座': self.wz_num,
            '硬卧': self.yw_num,
            '二等座': self.edz_num,
            '一等座': self.ydz_num,
            '商务特等座': self.swz_num,
            '动卧': self.dw_num
        }


class LeftTicketDTO:
    """
    查询余票参数信息
    """
    def __init__(self, train_date=datetime.date.today().strftime('%Y-%m-%d'), from_station='SZQ',
                 to_station='GZQ', purpose_codes='ADULT'):
        self.train_date = train_date
        self.from_station = from_station
        self.to_station = to_station
        self.purpose_codes = purpose_codes

    def encode_url_para(self):
        paras = ['train_date=' + self.train_date,
                 'from_station=' + self.from_station,
                 'to_station=' + self.to_station]
        p = '&'.join(['leftTicketDTO.' + v for v in paras])
        return p + '&purpose_codes=' + self.purpose_codes


def get_trains(dto):
    """
    获取余票信息
    :param dto:
    :return:
    """
    # 余票查询链接地址可能会变,其中结尾处的Z可能会被替换为[A-Z],注意!!!
    url = 'https://kyfw.12306.cn/otn/leftTicket/queryZ'
    url = url + '?' + dto.encode_url_para()
    # 请求头
    hds = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/74.0.3729.157 Safari/537.36',
        'Cookie': 'JSESSIONID=F8909A46584B13B74DE18535CC9DFE4;'
    }
    try:
        trains = []
        print('正在查询余票信息:', url)
        # 访问请求链接
        rsp = requests.get(url, headers=hds)
        text = json.loads(rsp.content.decode())

        # 检查返回码
        if text['httpstatus'] != 200:
            print('获取余票信息异常,', text)
            return

        # 获取想要的数据
        result = text['data']['result']
        print('共获取到{0}趟车次信息:'.format(len(result)))
        for v in result:
            item = [f for f in v.split('|')]
            t = Train(item)
            trains.append(t)
    except Exception as e:
        print('获取数据异常, {0} -> {1}'.format(Exception, e))
    return trains


def test():
    """
    测试代码
    :return:
    """
    import prettytable

    # 获取票信息
    para = LeftTicketDTO(from_station='IOQ', to_station='SNH', train_date='2020-01-15')
    trains = get_trains(para)
    if not trains:
        print('未找到符合条件的车次信息')
        return

    # 组装信息
    tab = prettytable.PrettyTable()
    # 标题头
    tab.field_names = trains[0].key_info.keys()
    # 组装车次信息
    for t in trains:
        tab.add_row(t.key_info.values())
    # 表格显示车次信息
    print(tab)


if __name__ == '__main__':
    test()



测试结果:

火车票查询系统(一)——利用python实现监控12306余票查询_第3张图片

ok 以上就是12306余票查询第一篇实现,下一篇将介绍自动登录12306账号并进行下单操作,敬请期待哦~~~

创作不易,喜欢的话给喵喵赏点吧~(可怜眼神)
火车票查询系统(一)——利用python实现监控12306余票查询_第4张图片

你可能感兴趣的:(爬虫实战,12306抢票,python监控12306余票,过年抢票,抢票系统实现,过年回家)