12306

  • train.py
import configparser
import json
import os
import re
import time
from urllib.parse import quote
from urllib.parse import unquote

import requests

from my_12306.captcha import jsdati

'''
创建一个火车类
'''


class Train:

    def __init__(self):
        # 加载配置文件
        self.__conf = configparser.ConfigParser()
        self.__conf.read('conf.ini', encoding='utf-8')
        # 出发地
        self.__from_station = self.__conf.get('ticket', 'from_station')
        # 目的地
        self.__to_station = self.__conf.get('ticket', 'to_station')
        # 出发日
        self.__train_date = self.__conf.get('ticket', 'train_date')
        # 车站代码 截止2018-05-14 版本号: 1.9053
        with open('station_code.json', 'r', encoding='utf-8') as f:
            self.__station_code = json.load(f)
        self.img_path = os.path.join(os.path.dirname(__file__), 'code.png')
        # 模拟浏览器
        self.__headers = {
            'User-Agent': self.__conf.get('headers', 'user_agent'),
            'Referer': self.__conf.get('headers', 'referer'),
            'Host': self.__conf.get('headers', 'host'),
        }
        # 禁用警告
        requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
        # 创建 Session
        self.__TicketSession = requests.Session()
        self.__TicketSession.headers = self.__headers
        self.__TicketSession.verify = False

        self.__seat_type = '1'

    def get_train(self):
        temp_str = self.__TicketSession.get('https://kyfw.12306.cn/otn/leftTicket/init').text
        pre_url = re.compile("CLeftTicketUrl = '(.*?)'").search(temp_str).group(1)
        get_train_url = 'https://kyfw.12306.cn/otn/{}'.format(pre_url)
        get_train_params = {
            'leftTicketDTO.train_date': self.__train_date,
            'leftTicketDTO.from_station': self.__station_code[self.__from_station],
            'leftTicketDTO.to_station': self.__station_code[self.__to_station],
            'purpose_codes': 'ADULT'
        }
        response = self.__TicketSession.get(url=get_train_url, params=get_train_params)
        if response.status_code == 200:
            return response.json()

    def query_ticket_info(self):
        js_info = self.get_train()
        if not js_info['status']:
            print('65 -> 查询余票失败!')
            return
        trains_list = js_info['data']['result']
        for i in trains_list:
            lst = i.split('|')
            if lst[11] != 'Y':
                continue
            with open('conf.ini', 'w', encoding='utf-8') as f:
                self.__conf.set('query_ticket_info', 'secretstr', unquote(lst[0]))
                self.__conf.set('query_ticket_info', 'train_no', lst[2])
                self.__conf.set('query_ticket_info', 'station_train_code', lst[3])
                self.__conf.set('query_ticket_info', 'leftticket', unquote(lst[12]))
                self.__conf.set('query_ticket_info', 'location_code', lst[15])
                self.__conf.write(f)
            return

    def init_dc(self):
        url = 'https://kyfw.12306.cn/otn/confirmPassenger/initDc'
        data = {
            '_json_att': '',
        }
        response = self.__TicketSession.post(url=url, data=data)
        if response.status_code == 200:
            html = response.text
            # 获取REPEAT_SUBMIT_TOKEN
            regex = re.compile("var globalRepeatSubmitToken = '(\w+)'")
            repeat_submit_token = re.findall(regex, html)[0]
            # 获取key_check_isChange
            key_check_is_change = re.findall("key_check_isChange':'(\w+)','leftDetails'", html)[0]
            with open('conf.ini', 'w', encoding='utf-8') as f:
                self.__conf.set('special', 'repeat_submit_token', repeat_submit_token)
                self.__conf.set('special', 'key_check_ischange', key_check_is_change)
                self.__conf.write(f)

    def get_passenger(self):
        url = 'https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs'
        data = {
            '_json_att': '',
            'REPEAT_SUBMIT_TOKEN': self.__conf.get('special', 'repeat_submit_token'),
        }
        response = self.__TicketSession.post(url=url, data=data)
        # if response.status_code == 200:
        #     result = response.json()
        #     return result['data']['normal_passengers']

    def get_img(self):
        """
       获取验证码图片 -> 保存图片
       """
        img_url = 'https://kyfw.12306.cn/passport/captcha/captcha-image'
        img_data = {
            'login_site': 'E',
            'module': 'login',
            'rand': 'sjrand',
        }
        response = self.__TicketSession.get(url=img_url, params=img_data)
        if response.ok:
            with open(self.img_path, 'wb') as f:
                f.write(response.content)

    def check_img(self):
        """
        利用打码平台自动识别验证码
        验证码正确 -> 执行 登陆
        验证码错误 -> 重新获取验证码图片
        """
        # 利用打码平台自动识别验证码
        self.get_img()
        print('正在识别验证码...')
        code = jsdati.decode(self.img_path)
        print(code)
        check_img_url = 'https://kyfw.12306.cn/passport/captcha/captcha-check'
        check_img_data = {
            'answer': code,
            'login_site': 'E',
            'rand': 'sjrand'
        }
        response = self.__TicketSession.post(url=check_img_url, data=check_img_data)
        if response.status_code == 200:
            result = response.json()
            print('146 - >', result['result_message'])
            if result['result_code'] == '4':
                self.login()
            elif result['result_code'] == -4:
                exit(1)
            else:
                jsdati.report_error()
                time.sleep(1)
                self.check_img()

    def login(self):
        """
        登陆12306
        登陆成功 -> 执行 验证登陆
        """
        login_url = 'https://kyfw.12306.cn/passport/web/login'
        login_data = {
            'username': self.__conf.get('user_info', 'username'),
            'password': self.__conf.get('user_info', 'password'),
            'appid': 'otn',
        }
        response = self.__TicketSession.post(url=login_url, data=login_data)
        if response.status_code == 200:
            response.encoding = 'utf-8'  # 这里必须加, 否则抛出异常 JSONDecodeError
            result = response.json()
            print('170  ->', result['result_message'])
            if result['result_code'] == 0:
                self.check_login()

    def check_login(self):
        """
        验证登陆 -> 验证通过 newapptk -> 验证通过 username=自己的姓名 -> 再次 检测登陆
        :return:
        """
        check_login_url = 'https://kyfw.12306.cn/passport/web/auth/uamtk'
        check_login_data = {
            'appid': 'otn'
        }
        response = self.__TicketSession.post(url=check_login_url, data=check_login_data)
        if response.status_code == 200:
            result = response.json()
            print('186  ->', result['result_message'])
            if result['result_code'] == 0:
                newapptk = result['newapptk']
                check_login_url = 'https://kyfw.12306.cn/otn/uamauthclient'
                check_login_data = {
                    'tk': newapptk
                }
                response = self.__TicketSession.post(url=check_login_url, data=check_login_data)
                if response.status_code == 200:
                    print('195  ->', result['result_message'])
                    if result['result_code'] == 0:
                        self.check_user()

    def check_user(self):
        """
        检查用户登陆 -> 提交订单请求
        """
        check_user_url = 'https://kyfw.12306.cn/otn/login/checkUser'
        check_user_data = {
            '_json_att': ''
        }
        response = self.__TicketSession.post(url=check_user_url, data=check_user_data)
        if response.status_code == 200:
            result = response.json()
            if result['status']:
                print('211  ->', result)
                self.submit_order_request()

    def submit_order_request(self):
        self.query_ticket_info()
        url = 'https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest'
        data = {
            'secretStr': self.__conf.get('query_ticket_info', 'secretstr'),
            'train_date': self.__train_date,
            'back_train_date': time.strftime('%Y-%m-%d'),
            'tour_flag': 'dc',
            'purpose_codes': 'ADULT',
            'query_from_station_name': self.__from_station,
            'query_to_station_name': self.__to_station,
            'undefined': '',
        }
        response = self.__TicketSession.post(url=url, data=data)
        if response.status_code == 200:
            result = response.json()
            if result['status']:
                print('231  ->', result)
                self.check_order_info()

    def check_order_info(self):
        self.init_dc()
        # self.get_passenger()
        url = 'https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo'
        data = {
            'cancel_flag': '2',  # 固定值
            'bed_level_order_num': '000000000000000000000000000000',  # 固定值
            # passengerTicketStr 和oldPassengerStr 均为旅客信息字符串, 不要含有空格
            # 座位编号,0,票类型,乘客名,证件类型,证件号,手机号码,保存常用联系人(Y或N)
            'passengerTicketStr': self.__conf.get('passenger', 'passengerticketstr'),
            # 乘客名,证件类型,证件号,乘客类型
            'oldPassengerStr': self.__conf.get('passenger', 'oldpassengerstr'),
            'tour_flag': 'dc',
            'randCode': '',
            'whatsSelect': '1',
            '_json_att': '',
            'REPEAT_SUBMIT_TOKEN': self.__conf.get('special', 'repeat_submit_token'),
        }
        response = self.__TicketSession.post(url=url, data=data)
        if response.status_code == 200:
            result = response.json()
            if result['status']:
                print('255  ->', result)
                self.get_queue_count()

    def get_queue_count(self):
        url = 'https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount'
        tmp = time.strftime('%a %b %d %Y 00:00:00 %z', time.strptime(self.__train_date, '%Y-%m-%d')).split('+')
        data = {
            'train_date': tmp[0] + 'GMT+' + tmp[1] + ' (中国标准时间)',
            'train_no': self.__conf.get('query_ticket_info', 'train_no'),
            'stationTrainCode': self.__conf.get('query_ticket_info', 'station_train_code'),
            'seatType': self.__seat_type,
            'fromStationTelecode': self.__station_code[self.__from_station],
            'toStationTelecode': self.__station_code[self.__to_station],
            'leftTicket': quote(self.__conf.get('query_ticket_info', 'leftticket')),
            'purpose_codes': '00',
            'train_location': self.__conf.get('query_ticket_info', 'location_code'),
            '_json_att': '',
            'REPEAT_SUBMIT_TOKEN': self.__conf.get('special', 'repeat_submit_token'),
        }
        response = self.__TicketSession.post(url=url, data=data)
        if response.status_code == 200:
            result = response.json()
            if result['status']:
                print('278  ->', result)
                self.confirm_single_for_queue()

    def confirm_single_for_queue(self):
        url = 'https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue'
        data = {
            'passengerTicketStr': self.__conf.get('passenger', 'passengerticketstr'),
            'oldPassengerStr': self.__conf.get('passenger', 'oldpassengerstr'),
            'randCode': '',
            'purpose_codes': '00',
            'key_check_isChange': self.__conf.get('special', 'key_check_ischange'),
            'leftTicketStr': quote(self.__conf.get('query_ticket_info', 'leftticket')),
            'train_location': self.__conf.get('query_ticket_info', 'location_code'),
            'choose_seats': '',
            'seatDetailType': '000',
            'whatsSelect': '1',
            'roomType': '00',
            'dwAll': 'N',
            '_json_att': '',
            'REPEAT_SUBMIT_TOKEN': self.__conf.get('special', 'repeat_submit_token'),
        }
        response = self.__TicketSession.post(url=url, data=data)
        if response.status_code == 200:
            result = response.json()
            if result['status']:
                print('303  ->', result)
                self.query_wait_time()

    def query_wait_time(self):
        url = 'https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime'
        data = {
            'random': int(time.time() * 1000),
            'tourFlag': 'dc',
            '_json_att': '',
            'REPEAT_SUBMIT_TOKEN': self.__conf.get('special', 'repeat_submit_token'),
        }
        response = self.__TicketSession.get(url=url, params=data)
        if response.status_code == 200:
            result = response.json()
            if result['status']:
                print('318  ->', result)
                # response = self.__TicketSession.get(url=url, params=data)
                # if response.status_code == 200:
                #     result = response.json()
                #     if result['status']:
                #         print('323  ->', result['data']['orderId'])
                #         self.result_order_for_queue(result['data']['orderId'])

    def result_order_for_queue(self, order_sequence_no):
        url = 'https://kyfw.12306.cn/otn/confirmPassenger/resultOrderForDcQueue'
        data = {
            'orderSequence_no': order_sequence_no,
            '_json_att': '',
            'REPEAT_SUBMIT_TOKEN': self.__conf.get('special', 'repeat_submit_token'),
        }
        response = self.__TicketSession.post(url=url, data=data)
        if response.status_code == 200:
            result = response.json()
            if result['status']:
                print('337  ->', result)

    def buy_ticket(self):
        self.query_ticket_info()
        self.check_img()


if __name__ == '__main__':
    train = Train()
    train.buy_ticket()

12306_第1张图片
12306.jpg

最后更新时间:2018-05-15 0:15:23

你可能感兴趣的:(12306)