python查询12306票量

City.py文件用于获取并更新站名文件,同时提供查询站名和站名id的接口。

# <City.py>
# This file is for getting city names
import requests as r
import re
import os
import json


def get_version():
    url = 'https://www.12306.cn/index/index.html'
    res = r.get(url)
    res.encoding = 'gb2312'
    if res.status_code != 200:
        return 'error'
    result = re.search('<script src="script/core/common/station_name_v(.{5}).js"></script>', res.text)
    return result.group(1)


def create_list():
    url = 'https://www.12306.cn/index/script/core/common/station_name_v' + get_version() + '.js'
    res = r.get(url)
    info = re.search('var station_names =\'@(.*)\';', res.text)
    info = info.group(1)
    info = info.split('@')
    # 生成数据字典
    dict = {}
    for i in info:
        temp = i.split('|')
        dict[temp[2]] = temp[1]
    with open('station_name.txt', 'w') as f:
        f.write(str(dict))


def get_city():  # 查询数据字典,获得车站名称
    if not os.access('station_name.txt', os.F_OK) or not os.access('station_name.txt', os.R_OK):
        create_list()
    with open('station_name.txt', 'r') as f:
        info = f.read()
    info = info.replace('\'', '"')
    dict = json.loads(info, encoding='gb2312') # json键值必须是双引号
    return dict


def get_id(str):   # 获取车站id
    dict = get_city()
    for i in dict:
        if dict[i] == str:
            return i
    return 'no find'


'''
if __name__ == '__main__':
    print(get_city('VAP'))
'''

web_12306.py:

该文件实现的功能:

  1. 检查本地站名文件与官网是否一致,不一致将自动更新;

  2. 实现票量的查询。

from urllib.request import urlretrieve
import requests as r
import json
import os
import re
import configparser
from PIL import Image
import matplotlib.pyplot as plt
import City
import prettytable as pt
CLeftTickUrl = ''


def analysis_info(result):
    train_len = len(result)
    print('共查询到' + str(train_len) + '个车次')
    # print('\t车次\t起始站\t\t终点站\t 出发时间\t到站时间\t\t历时\t特等\t一等\t二等\t一等卧\t动卧\t二等卧\t硬座\t无座\t\t日期')
    tb = pt.PrettyTable('车次 起始站 终点站 出发时间 到站时间 历时 特等 一等 二等 一等卧 动卧 二等卧 硬座 无座 日期'.split(' '))
    dict = City.get_city()
    for i in range(train_len):
        data = result[i].split('|')
        if len(data[3]) != 5:
            for i in range(5-len(data[3])):
                data[3] = data[3] + ' '
        '''print('\t' + data[3] + '\t' + dict[data[6]] + '\t\t' + dict[data[7]] + '\t  ' + data[8] + '\t\t ' + data[9] + '\t   ' +
              data[10] + '\t ' + data[32] + '\t\t ' + data[31] + '\t\t ' + data[30] + '\t\t ' + data[23] + '\t\t ' +
              data[33] + '\t\t ' + data[28] + '\t\t ' + data[29] + '\t\t ' + data[26] + '\t\t  ' + data[13])'''
        tb.add_row([data[3], dict[data[6]], dict[data[7]], data[8], data[9], data[10], data[32], data[31], data[30], data[23],
                    data[33], data[28], data[29], data[26], data[13]])
    print(tb) # 控制台可对齐输出


def query_train():
    global CLeftTickUrl
    print('************注意:12306网站允许提前购买30天内的车票************')
    date = input('请输入日期,格式如xxxx-xx-xx:\n')
    while True:
        from_s = input('请输入起始站:\n')
        from_id = City.get_id(from_s)
        if from_id == 'no find':
            print('未找到该站名,请重新输入')
            continue
        else:
            break
    while True:
        to_s = input('请输入终点站:\n')
        to_id = City.get_id(to_s)
        if to_id == 'no find':
            print('未找到该站名,请重新输入')
            continue
        else:
            break

    url = 'https://kyfw.12306.cn/otn/' + CLeftTickUrl + '?' \
          'leftTicketDTO.train_date=' + date + '&leftTicketDTO.from_station=' + from_id + '&leftTicketDTO.to_station=' +\
          to_id + '&purpose_codes=ADULT'
    header = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
    }
    session = r.session()
    session.get('https://kyfw.12306.cn/otn/leftTicket/init?',headers=header)
    response = session.get(url, headers=header)
    response.encoding = 'utf-8'

    if '网络可能存在问题' in response.text:
        print('查询失败,请检查日期是否输入正确!')
        exit(-1)
    # 转化为数据字典
    dict = json.loads(response.text)
    result = dict['data']['result']
    analysis_info(result)
    return 0


def initialization():
    version = City.get_version()
    if version == 'error':
        print('获取失败,请检查网络连接是否正常')
        exit(-1)
    print('************当前12306联网车站数据版本为 V' + version + '************')
    if os.access('station_name.txt', os.F_OK):
        # 判断与12306的车站数据版本是否一致,不一致则需要更新
        if os.access('set.conf', os.F_OK) and os.access('set.conf', os.R_OK):
            config = configparser.RawConfigParser()
            config.read('set.conf')
            version_t = config.get('Basic', 'Version')
            if version_t == version:
                print('************本地车站数据版本 V' + version_t + ' 与联网版本一致,无需更新************')
            else:
                City.create_list()
                print('************已成功更新!当前本地车站数据版本为 V' + version + '************')
                config = configparser.RawConfigParser()
                config.add_section('Basic')
                config.set('Basic', 'Version', version)
                with open('set.conf', 'w') as configfile:
                    config.write(configfile)
        else:
            City.create_list()
            print('************本地车站数据版本为 V' + version + '************')
            config = configparser.RawConfigParser()
            config.add_section('Basic')
            config.set('Basic', 'Version', version)
            with open('set.conf', 'w') as configfile:
                config.write(configfile)
    else:
        City.create_list()
        print('************本地车站数据版本为 V' + version + '************')
        config = configparser.RawConfigParser()
        config.add_section('Basic')
        config.set('Basic', 'Version', version)
        with open('set.conf', 'w') as configfile:
            config.write(configfile)
    global CLeftTickUrl
    url = 'https://kyfw.12306.cn/otn/leftTicket/init'
    try:
        res = r.get(url)
        result = re.search('var CLeftTicketUrl = \'(.*)\';',res.text)
    except:
        print('Error:参数获取失败,将退出程序')
        exit(-1)
    else:
        CLeftTickUrl = result.group(1)


if __name__ == '__main__':
    initialization()
    query_train()

运行截图: