본문 바로가기
프로젝트/SRT&KTX 매진표 예매

SRT&KTX 기차표 매크로 예매 - (4) SRT 승차권 예매

by 매크로메이커 2024. 1. 9.

마지막으로 승차권 예매를 해보자!

승차권 예매

예매 request는 내용도 많고 복잡하지만... 침착하게 분석해보자

Body data를 분석하다보니, 입석+좌석이나 환승의 경우 데이터가 너무 복잡해서, 직행 열차만 다룬다.

 

  • URL : https://etk.srail.kr/hpg/hra/01/checkUserInfo.do
  • Method : Post

Body Data

  • rsvTpCd : 예약 타입 코드 (01 - 일반 예약, 05 - 대기)
  • jobId : ??? (1101 - 일반 예약, 1102 - 대기)
  • jrnyTpCd : [추정] 여행 타입 코드 - 항상 '11'
  • jrnyCnt : [추정] 여정 개수 - 항상 '1'
  • totPrnb : 총 탑승 인원
  • stndFlg : [추정] 입석 여부 - 항상 'N'
  • trnOrdrNo1, jrnySqno1, runDt1, trnNo1, trnGpCd1, stlbTrnClsfCd1, dptDt1, dptTm1, dptRsStnCd1, dptStnConsOrdr1, dptStnRunOrdr1, arvRsStnCd1, arvStnConsOrdr1, arvStnRunOrdr1
  • scarYn1 : 좌석 지정 여부 - 항상 'N'
  • scarGridcnt1, scarNo1, seatNo1_1, seatNo1_2, seatNo1_3, seatNo1_4, seatNo1_5, seatNo1_6, seatNo1_7, seatNo1_8, seatNo1_9 : 좌석 지정이 없으므로 모두 공백
  • psrmClCd1 : 좌석 등급 (1 - 일반실, 2 - 특실)
  • smkSeatAttCd1 : [추정] 항상 000
  • dirSeatAttCd1 : [추정] 항상 000
  • locSeatAttCd1 : 좌석위치 (000 - default, 011 - 1인석, 012 - 창측좌석, 013 - 내측좌석)
  • rqSeatAttCd1 : 좌석속성 (015 - 일반, 021 - 휠체어, 028 - 전동휠체어)
  • etcSeatAttCd1 : [추정] 항상 000
  • jrnyTpCd1, jrnyTpCd2, trnOrdrNo2, jrnySqno2, runDt2, trnNo2, trnGpCd2, stlbTrnClsfCd2, dptDt2, dptTm2, dptRsStnCd2, dptStnConsOrdr2, dptStnRunOrdr2, arvRsStnCd2, arvStnConsOrdr2, arvStnRunOrdr2, scarYn2, scarGridcnt2, scarNo2, seatNo2_1, seatNo2_2, seatNo2_3, seatNo2_4, seatNo2_5, seatNo2_6, seatNo2_7, seatNo2_8, seatNo2_9, psrmClCd2, smkSeatAttCd2, dirSeatAttCd2, locSeatAttCd2, rqSeatAttCd2, etcSeatAttCd2 : 모두 공백 (환승 없음)
  • psgGridcnt=1
  • psgTpCd1=1
  • psgInfoPerPrnb1=1
  • psgTpCd2=
  • psgInfoPerPrnb2=
  • psgTpCd3=
  • psgInfoPerPrnb3=
  • psgTpCd4=
  • psgInfoPerPrnb4=
  • psgTpCd5=
  • psgInfoPerPrnb5=
  • mutMrkVrfCd=
  • reqTime=1704796578230
  • crossYn=N
def book_ticket(self, adult, child, senior, svrDsb, mldDsb, train_schedule, locSeatAttCd, rqSeatAttCd,
                isReservation = False, isBusiness = False):
    check_user_url = "https://etk.srail.kr/hpg/hra/01/checkUserInfo.do"

    psgGridcnt = 0
    psgGrid = []
    if adult > 0:
        psgGridcnt += 1
        psgGrid.append(["1", str(adult)])
    if child > 0:
        psgGridcnt += 1
        psgGrid.append(["5", str(child)])
    if senior > 0:
        psgGridcnt += 1
        psgGrid.append(["4", str(senior)])
    if svrDsb > 0:
        psgGridcnt += 1
        psgGrid.append(["2", str(svrDsb)])
    if mldDsb > 0:
        psgGridcnt += 1
        psgGrid.append(["3", str(mldDsb)])

    body = {
        "rsvTpCd": "05" if isReservation else "01",
        "jobId": "1102" if isReservation else "1101",
        "jrnyTpCd": "11",
        "jrnyCnt": "1",
        "totPrnb": str(adult+child+senior+svrDsb+mldDsb),
        "stndFlg": "N",
        "trnOrdrNo1": train_schedule['trnOrdrNo'],
        "jrnySqno1": train_schedule['jrnySqno'],
        "runDt1": train_schedule['runDt'],
        "trnNo1": train_schedule['trnNo'],
        "trnGpCd1": train_schedule['trnGpCd'],
        "stlbTrnClsfCd1": train_schedule['stlbTrnClsfCd'],
        "dptDt1": train_schedule['dptDt'],
        "dptTm1": train_schedule['dptTm'],
        "dptRsStnCd1": train_schedule['dptRsStnCd'],
        "dptStnConsOrdr1": train_schedule['dptStnConsOrdr'],
        "dptStnRunOrdr1": train_schedule['dptStnRunOrdr'],
        "arvRsStnCd1": train_schedule['arvRsStnCd'],
        "arvStnConsOrdr1": train_schedule['arvStnConsOrdr'],
        "arvStnRunOrdr1": train_schedule['arvStnRunOrdr'],
        "scarYn1": "N",
        "scarGridcnt1": "",
        "scarNo1": "",
        "seatNo1_1": "",
        "seatNo1_2": "",
        "seatNo1_3": "",
        "seatNo1_4": "",
        "seatNo1_5": "",
        "seatNo1_6": "",
        "seatNo1_7": "",
        "seatNo1_8": "",
        "seatNo1_9": "",
        "psrmClCd1": "2" if isBusiness else "1",
        "smkSeatAttCd1": "000",
        "dirSeatAttCd1": "000",
        "locSeatAttCd1": locSeatAttCd,  # 좌석위치 (000 - default, 011 - 1인석, 012 - 창측좌석, 013 - 내측좌석)
        "rqSeatAttCd1": rqSeatAttCd,    # 좌석속성 (015 - 일반, 021 - 휠체어, 028 - 전동휠체어)
        "etcSeatAttCd1": "000",
        "jrnyTpCd1": "",
        "jrnyTpCd2": "",
        "trnOrdrNo2": "",
        "jrnySqno2": "",
        "runDt2": "",
        "trnNo2": "",
        "trnGpCd2": "",
        "stlbTrnClsfCd2": "",
        "dptDt2": "",
        "dptTm2": "",
        "dptRsStnCd2": "",
        "dptStnConsOrdr2": "",
        "dptStnRunOrdr2": "",
        "arvRsStnCd2": "",
        "arvStnConsOrdr2": "",
        "arvStnRunOrdr2": "",
        "scarYn2": "",
        "scarGridcnt2": "",
        "scarNo2": "",
        "seatNo2_1": "",
        "seatNo2_2": "",
        "seatNo2_3": "",
        "seatNo2_4": "",
        "seatNo2_5": "",
        "seatNo2_6": "",
        "seatNo2_7": "",
        "seatNo2_8": "",
        "seatNo2_9": "",
        "psrmClCd2": "",
        "smkSeatAttCd2": "",
        "dirSeatAttCd2": "",
        "locSeatAttCd2": "",
        "rqSeatAttCd2": "",
        "etcSeatAttCd2": "",
        "psgGridcnt": psgGridcnt,
        "psgTpCd1": "" if len(psgGrid) < 1 else psgGrid[0][0],
        "psgInfoPerPrnb1": "" if len(psgGrid) < 1 else psgGrid[0][1],
        "psgTpCd2": "" if len(psgGrid) < 2 else psgGrid[1][0],
        "psgInfoPerPrnb2": "" if len(psgGrid) < 2 else psgGrid[1][1],
        "psgTpCd3": "" if len(psgGrid) < 3 else psgGrid[2][0],
        "psgInfoPerPrnb3": "" if len(psgGrid) < 3 else psgGrid[2][1],
        "psgTpCd4": "" if len(psgGrid) < 4 else psgGrid[3][0],
        "psgInfoPerPrnb4": "" if len(psgGrid) < 4 else psgGrid[3][1],
        "psgTpCd5": "" if len(psgGrid) < 5 else psgGrid[4][0],
        "psgInfoPerPrnb5": "" if len(psgGrid) < 5 else psgGrid[4][1],
        "mutMrkVrfCd": "",
        "reqTime": str(int(time.time())*1000),
        "crossYn": "N"
    }
    res = self.session.post(check_user_url, data=body)
    
    print(res.text)

 

예매 Requet를 checkUserInfo.do로 보내는데, 요청을 보내보면 redirection 링크를 돌려받는데, 케이스가 두 가지로 나뉘는 것을 볼 수있다.

1. 로그인이 된 경우

location.replace('/hpg/hra/02/requestReservationInfo.do?pageId=TK0101030000');

2. 로그인이 안 된 경우

location.replace('/cmc/01/selectLoginForm.do?pageId=TK0701000000&rsvTpCd=~~~');

 

checkUserInfo.do는 로그인 여부만 확인하고, 실제 예약은 requestReservationInfo.do에서 진행됨을 알 수 있다.

이를 참고하여 살을 덧붙여보자.

 

    check_user_res = self.session.post(check_user_url, data=body)
    if 'selectLoginForm' in check_user_res.text:
        if not self.login():
            raise '예약 중 로그인 재시도 실패'        # 로그인 실패
        check_user_res = self.session.post('https://etk.srail.kr/hpg/hra/01/checkUserInfo.do', data=body)
        if 'requestReservationInfo' in check_user_res.text:
            reservation_res = self.session.post(reservation_url, data=body)
        else:
            raise f'예약 중 재로그인 후 예상치 못한 user check fail - \n{check_user_res.text}'
    elif 'requestReservationInfo' in check_user_res.text:
        reservation_res = self.session.post(reservation_url, data=body)
    else:
        raise f'예약 중 예상치 못한 user check fail - \n{check_user_res.text}'
    print(reservation_res.text)

 

예약 요청을 하고 나면 다시 리디렉션 메세지를 받는다.

location.replace('confirmReservationInfo.do?pageId=null');

리디렉션 페이지에서 에러나는 경우는 두 가지로 확인된다.

1. 잔여석없음

2. 예약대기자한도수초과

 

위 케이스 외에는 정상 예매가 됐다고 판단하자.

 

    if 'confirmReservationInfo' in reservation_res.text:
        confirm_res = self.session.post(confirm_url, data=body)
    else:
        raise f'예약 중 reservation fail - \n{check_user_res.text}'

    if "잔여석없음" in confirm_res.text:
        raise "잔여석 없음"
    elif "예약대기자한도수초과" in confirm_res.text:
        raise "예약대기자 한도수 초과"

    print("예약 성공")
    return True

 


완성된 코드다...!

제일 아래 main 함수에서 코드만 조금 만져주면, 예약까지 문제없이 가능하다. 

 

import re
import time

import requests
from bs4 import BeautifulSoup

class SRT:
    def __init__(self, login_type, login_id, login_pwd):
        # login_type - 1: 회원번호, 2: 이메일, 3: 휴대전화번호
        # login_id - 회원번호/이메일/휴대전화번호
        # login_pwd - 비밀번호

        self.session = requests.Session()
        self.session.get("https://etk.srail.kr/main.do")

        self.login_type = login_type
        self.login_id = login_id
        self.login_pwd = login_pwd

        self.stations = self.fetch_stations()

    def login(self):
        login_url = "https://etk.srail.kr/cmc/01/selectLoginInfo.do"
        body = {
            "rsvTpCd": "",
            "goUrl": "",
            "from": "",
            "srchDvCd": self.login_type,
            "srchDvNm": self.login_id,
            "hmpgPwdCphd": self.login_pwd
        }
        res = self.session.post(login_url, data=body)

        if "location.replace('/main.do')" in res.text:
            return True
        else:
            return False

    def is_logged_in(self):
        res = self.session.get("https://etk.srail.kr/main.do")

        if '로그아웃' in res.text:
            return True
        else:
            return False

    def fetch_schedule(self, dptRsStnCdNm, arvRsStnCdNm, dptDt, dptTm, adult, child, senior, svrDsb, mldDsb,
                       chtnDvCd='1', locSeatAttCd1='000', rqSeatAttCd1='015', trnGpCd='300', dlayTnumAplFlg='Y'):
        schedule_url = "https://etk.srail.kr/hpg/hra/01/selectScheduleList.do"
        body = {
            "dptRsStnCd": self.stations[dptRsStnCdNm],          # 출발역 코드 (e.g. 0551)
            "arvRsStnCd": self.stations[arvRsStnCdNm],          # 도착역 코드
            "stlbTrnClsfCd": "05",                              # [추정] 항상 '05' - ~~ Train Classification Code 같은데, 변하지 않음
            "psgNum": str(adult+child+senior+svrDsb+mldDsb),    # 총 승객 인원
            "seatAttCd": rqSeatAttCd1,                          # [추정] 좌석 속성, rqSeatAttCd1와 같은 값인 것으로 보임
            "isRequest": "Y",                                   # [추정] 항상 'Y'
            "dptRsStnCdNm": dptRsStnCdNm,                       # 출발역 이름
            "arvRsStnCdNm": arvRsStnCdNm,                       # 도착역 이름
            "dptDt": dptDt,                                     # 출발 날짜 (e.g. 20240108)
            "dptTm": dptTm,                                     # 출발 시간 (e.g. 194500)
            "chtnDvCd": chtnDvCd,                               # 여정경로 (1 - 직통, 2 - 환승, 3 - 왕복)
            "psgInfoPerPrnb1": str(adult),                      # 어른 인원
            "psgInfoPerPrnb5": str(child),                      # 어린이 인원
            "psgInfoPerPrnb4": str(senior),                     # 노인 인원
            "psgInfoPerPrnb2": str(svrDsb),                     # 중증장애인 인원
            "psgInfoPerPrnb3": str(mldDsb),                     # 경증장애인 인원
            "locSeatAttCd1": locSeatAttCd1,                     # 좌석위치 (000 - default, 011 - 1인석, 012 - 창측좌석, 013 - 내측좌석)
            "rqSeatAttCd1": rqSeatAttCd1,                       # 좌석속성 (015 - 일반, 021 - 휠체어, 028 - 전동휠체어)
            "trnGpCd": trnGpCd,                                 # 차종구분 (109 - 전체, 300 - SRT, 900 - SRT+KTX)
            "dlayTnumAplFlg": dlayTnumAplFlg,                   # 지연열차포함 (Y - 포함, N - 미포함)
        }
        res = self.session.post(schedule_url, data=body)

        soup = BeautifulSoup(res.text, 'html.parser')
        trains = soup.find_all("td", {"class": "trnNo"})

        result = []
        for tr in trains:
            schedule_info = dict()
            schedule_info["trnOrdrNo"] = tr.find("input", {"name": re.compile(r'trnOrdrNo')})['value']
            schedule_info["jrnySqno"] = tr.find("input", {"name": re.compile(r'jrnySqno')})['value']
            schedule_info["runDt"] = tr.find("input", {"name": re.compile(r'runDt')})['value']
            schedule_info["trnNo"] = tr.find("input", {"name": re.compile(r'trnNo')})['value']
            schedule_info["trnGpCd"] = tr.find("input", {"name": re.compile(r'trnGpCd')})['value']
            schedule_info["stlbTrnClsfCd"] = tr.find("input", {"name": re.compile(r'stlbTrnClsfCd')})['value']
            schedule_info["dptDt"] = tr.find("input", {"name": re.compile(r'dptDt')})['value']
            schedule_info["dptTm"] = tr.find("input", {"name": re.compile(r'dptTm')})['value']
            schedule_info["dptRsStnCd"] = tr.find("input", {"name": re.compile(r'dptRsStnCd')})['value']
            schedule_info["dptRsStnCdNm"] = tr.find("input", {"name": re.compile(r'dptRsStnCdNm')})['value']
            schedule_info["dptStnConsOrdr"] = tr.find("input", {"name": re.compile(r'dptStnConsOrdr')})['value']
            schedule_info["dptStnRunOrdr"] = tr.find("input", {"name": re.compile(r'dptStnRunOrdr')})['value']
            schedule_info["arvRsStnCd"] = tr.find("input", {"name": re.compile(r'arvRsStnCd')})['value']
            schedule_info["arvRsStnCdNm"] = tr.find("input", {"name": re.compile(r'arvRsStnCdNm')})['value']
            schedule_info["arvStnConsOrdr"] = tr.find("input", {"name": re.compile(r'arvStnConsOrdr')})['value']
            schedule_info["arvStnRunOrdr"] = tr.find("input", {"name": re.compile(r'arvStnRunOrdr')})['value']
            schedule_info["seatAttCd"] = tr.find("input", {"name": re.compile(r'seatAttCd')})['value']
            schedule_info["scarGridcnt"] = tr.find("input", {"name": re.compile(r'scarGridcnt')})['value']
            schedule_info["scarNo"] = tr.find("input", {"name": re.compile(r'scarNo')})['value']
            schedule_info["seatNo_1"] = tr.find("input", {"name": re.compile(r'seatNo_1')})['value']
            schedule_info["seatNo_2"] = tr.find("input", {"name": re.compile(r'seatNo_2')})['value']
            schedule_info["seatNo_3"] = tr.find("input", {"name": re.compile(r'seatNo_3')})['value']
            schedule_info["seatNo_4"] = tr.find("input", {"name": re.compile(r'seatNo_4')})['value']
            schedule_info["seatNo_5"] = tr.find("input", {"name": re.compile(r'seatNo_5')})['value']
            schedule_info["seatNo_6"] = tr.find("input", {"name": re.compile(r'seatNo_6')})['value']
            schedule_info["seatNo_7"] = tr.find("input", {"name": re.compile(r'seatNo_7')})['value']
            schedule_info["seatNo_8"] = tr.find("input", {"name": re.compile(r'seatNo_8')})['value']
            schedule_info["seatNo_9"] = tr.find("input", {"name": re.compile(r'seatNo_9')})['value']
            schedule_info["trainDiscGenRt"] = tr.find("input", {"name": re.compile(r'trainDiscGenRt')})['value']
            schedule_info["rcvdAmt"] = tr.find("input", {"name": re.compile(r'rcvdAmt')})['value']
            schedule_info["rcvdFare"] = tr.find("input", {"name": re.compile(r'rcvdFare')})['value']
            schedule_info["trnNstpLeadInfo"] = tr.find("input", {"name": re.compile(r'trnNstpLeadInfo')})['value']

            result.append(schedule_info)

        return result

    def fetch_stations(self):
        res = self.session.get("https://etk.srail.kr/hpg/hra/01/selectMapInfo.do")

        soup = BeautifulSoup(res.text, 'html.parser')
        stations = soup.find_all("a", {"class": re.compile(r'map')})

        result = dict()
        for station in stations:
            station_split = station['onclick'].split("'")
            result[station_split[3]] = station_split[1]
        return result

    def book_ticket(self, adult, child, senior, svrDsb, mldDsb, train_schedule, locSeatAttCd, rqSeatAttCd,
                    isReservation = False, isBusiness = False):
        check_user_url = "https://etk.srail.kr/hpg/hra/01/checkUserInfo.do"
        reservation_url = "https://etk.srail.kr/hpg/hra/02/requestReservationInfo.do"
        confirm_url = "https://etk.srail.kr/hpg/hra/02/confirmReservationInfo.do"

        psgGridcnt = 0
        psgGrid = []
        if adult > 0:
            psgGridcnt += 1
            psgGrid.append(["1", str(adult)])
        if child > 0:
            psgGridcnt += 1
            psgGrid.append(["5", str(child)])
        if senior > 0:
            psgGridcnt += 1
            psgGrid.append(["4", str(senior)])
        if svrDsb > 0:
            psgGridcnt += 1
            psgGrid.append(["2", str(svrDsb)])
        if mldDsb > 0:
            psgGridcnt += 1
            psgGrid.append(["3", str(mldDsb)])

        body = {
            "rsvTpCd": "05" if isReservation else "01",
            "jobId": "1102" if isReservation else "1101",
            "jrnyTpCd": "11",
            "jrnyCnt": "1",
            "totPrnb": str(adult+child+senior+svrDsb+mldDsb),
            "stndFlg": "N",
            "trnOrdrNo1": train_schedule['trnOrdrNo'],
            "jrnySqno1": train_schedule['jrnySqno'],
            "runDt1": train_schedule['runDt'],
            "trnNo1": train_schedule['trnNo'],
            "trnGpCd1": train_schedule['trnGpCd'],
            "stlbTrnClsfCd1": train_schedule['stlbTrnClsfCd'],
            "dptDt1": train_schedule['dptDt'],
            "dptTm1": train_schedule['dptTm'],
            "dptRsStnCd1": train_schedule['dptRsStnCd'],
            "dptStnConsOrdr1": train_schedule['dptStnConsOrdr'],
            "dptStnRunOrdr1": train_schedule['dptStnRunOrdr'],
            "arvRsStnCd1": train_schedule['arvRsStnCd'],
            "arvStnConsOrdr1": train_schedule['arvStnConsOrdr'],
            "arvStnRunOrdr1": train_schedule['arvStnRunOrdr'],
            "scarYn1": "N",
            "scarGridcnt1": "",
            "scarNo1": "",
            "seatNo1_1": "",
            "seatNo1_2": "",
            "seatNo1_3": "",
            "seatNo1_4": "",
            "seatNo1_5": "",
            "seatNo1_6": "",
            "seatNo1_7": "",
            "seatNo1_8": "",
            "seatNo1_9": "",
            "psrmClCd1": "2" if isBusiness else "1",
            "smkSeatAttCd1": "000",
            "dirSeatAttCd1": "000",
            "locSeatAttCd1": locSeatAttCd,  # 좌석위치 (000 - default, 011 - 1인석, 012 - 창측좌석, 013 - 내측좌석)
            "rqSeatAttCd1": rqSeatAttCd,    # 좌석속성 (015 - 일반, 021 - 휠체어, 028 - 전동휠체어)
            "etcSeatAttCd1": "000",
            "jrnyTpCd1": "",
            "jrnyTpCd2": "",
            "trnOrdrNo2": "",
            "jrnySqno2": "",
            "runDt2": "",
            "trnNo2": "",
            "trnGpCd2": "",
            "stlbTrnClsfCd2": "",
            "dptDt2": "",
            "dptTm2": "",
            "dptRsStnCd2": "",
            "dptStnConsOrdr2": "",
            "dptStnRunOrdr2": "",
            "arvRsStnCd2": "",
            "arvStnConsOrdr2": "",
            "arvStnRunOrdr2": "",
            "scarYn2": "",
            "scarGridcnt2": "",
            "scarNo2": "",
            "seatNo2_1": "",
            "seatNo2_2": "",
            "seatNo2_3": "",
            "seatNo2_4": "",
            "seatNo2_5": "",
            "seatNo2_6": "",
            "seatNo2_7": "",
            "seatNo2_8": "",
            "seatNo2_9": "",
            "psrmClCd2": "",
            "smkSeatAttCd2": "",
            "dirSeatAttCd2": "",
            "locSeatAttCd2": "",
            "rqSeatAttCd2": "",
            "etcSeatAttCd2": "",
            "psgGridcnt": psgGridcnt,
            "psgTpCd1": "" if len(psgGrid) < 1 else psgGrid[0][0],
            "psgInfoPerPrnb1": "" if len(psgGrid) < 1 else psgGrid[0][1],
            "psgTpCd2": "" if len(psgGrid) < 2 else psgGrid[1][0],
            "psgInfoPerPrnb2": "" if len(psgGrid) < 2 else psgGrid[1][1],
            "psgTpCd3": "" if len(psgGrid) < 3 else psgGrid[2][0],
            "psgInfoPerPrnb3": "" if len(psgGrid) < 3 else psgGrid[2][1],
            "psgTpCd4": "" if len(psgGrid) < 4 else psgGrid[3][0],
            "psgInfoPerPrnb4": "" if len(psgGrid) < 4 else psgGrid[3][1],
            "psgTpCd5": "" if len(psgGrid) < 5 else psgGrid[4][0],
            "psgInfoPerPrnb5": "" if len(psgGrid) < 5 else psgGrid[4][1],
            "mutMrkVrfCd": "",
            "reqTime": str(int(time.time())*1000),
            "crossYn": "N"
        }
        check_user_res = self.session.post(check_user_url, data=body)
        if 'selectLoginForm' in check_user_res.text:
            if not self.login():
                raise '예약 중 로그인 재시도 실패'        # 로그인 실패
            check_user_res = self.session.post('https://etk.srail.kr/hpg/hra/01/checkUserInfo.do', data=body)

            if 'requestReservationInfo' in check_user_res.text:
                reservation_res = self.session.post(reservation_url, data=body)
            else:
                raise f'예약 중 재로그인 후 user check fail - \n{check_user_res.text}'
        elif 'requestReservationInfo' in check_user_res.text:
            reservation_res = self.session.post(reservation_url, data=body)
        else:
            raise f'예약 중 user check fail - \n{check_user_res.text}'

        if 'confirmReservationInfo' in reservation_res.text:
            confirm_res = self.session.post(confirm_url, data=body)
        else:
            raise f'예약 중 reservation fail - \n{check_user_res.text}'

        if "잔여석없음" in confirm_res.text:
            raise "잔여석 없음"
        elif "예약대기자한도수초과" in confirm_res.text:
            raise "예약대기자 한도수 초과"

        print("예약 성공")
        return True


if __name__ == "__main__":
    srt = SRT('1', '', '')
    srt.login()
    schedules = srt.fetch_schedule('수서', '부산', '20240112', '053100', 1, 0, 0, 0, 0)

    srt.book_ticket(1, 0, 0, 0, 0, schedules[0], '000', '015', True)