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

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

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

KTX의 마지막..! 기차표를 예매해보자

 

KTX 예매 POST Request

ktx 예매를 해보면, 꽤 많은 데이터를 전달하는 것을 확인할 수 있다. 

 

스케쥴 조회했던 것과 마찬가지로 테스트해가며 하나씩 분석해보자..

예매를 여러번 해보면서 찾은 결과

Data Field

  • selGoTrain : 기차 종류 (05 - 전체, 00 - KTX, 09 - ITX-청춘, 18 - ITX-마음, 02 - 무궁화, 03 - 통근열차)
  •  
  • txtSeatAttCd2 : 좌석방향 (000 - 기본, 009 - 순방향석, 010 - 역방향석)
  • txtSeatAttCd3 : 좌석위치 (000 - 기본, 011 - 1인석, 012 - 창측좌석, 013 - 내측좌석)
  • txtSeatAttCd4 : 좌석속성
  • txtTotPsgCnt : 총 인원 수
  • txtPsgTpCd1 : 고정값 - 1
  • txtPsgTpCd2 : 고정값 - 3
  • txtPsgTpCd3 : 고정값 - 1
  • txtPsgTpCd5 : 고정값 - 1
  • txtPsgTpCd7 : 고정값 - 1
  • txtPsgTpCd8 : 고정값 - 3
  • txtCompaCnt1 :  일반 어른 수
  • txtCompaCnt2 : 일반 어린이 수
  • txtCompaCnt3 : 중증 장애인 수
  • txtCompaCnt5 : 노인 수
  • txtCompaCnt7 : 경증 장애인 수
  • txtCompaCnt8 : 유아 수
  • txtJobId :  예약타입(1101 - 일반, 1102 - 예약, 1103 - 좌석선택)
  • txtJrnyCnt : 여정 개수 - 1
  • txtPsrmClCd1 :  좌석 등급 (1-일반실, 2-특실)
  • txtJrnySqno1 : 여정경로 (001 - 직통)
  • txtJrnyTpCd1 : 여정타입 (11 - 편도)
  • txtDptDt1 : 출발일 - 스케쥴 정보에 포함
  • txtDptRsStnCd1 : 출발역 코드 - 스케쥴 정보에 포함
  • txtDptRsStnCdNm1 : 출발역 이름 - 스케쥴 정보에 포함
  • txtDptTm1 : 출발 시간 - 스케쥴 정보에 포함
  • txtArvRsStnCd1 : 도착역 코드 - 스케쥴 정보에 포함
  • txtArvRsStnCdNm1 : 도착역 이름 - 스케쥴 정보에 포함
  • txtArvTm1 : 열차 도착 시간 - 스케쥴 정보에 포함
  • txtTrnNo1 : 열차 번호 - 스케쥴 정보에 포함
  • txtRunDt1 : 출발일 - 스케쥴 정보에 포함
  • txtTrnClsfCd1 : 열차 종류 - 스케쥴 정보에 포함
  • txtTrnGpCd1 : 기차 종류 - 스케쥴 정보에 포함

전달하는 데이터 중에 strHmac등 암호화 관련된 코드가 있긴 했으나, 실제 예매를 하는데는 아무 영향이 없었다.

 

위 정보로 완성된 예약 코드..!

def book_ticket(self, adult, child, baby, senior, svrDsb, mldDsb, train_schedule, txtSeatAttCd_3='000',
                txtSeatAttCd_2='000', txtSeatAttCd_4='015', isReservation=False, isBusiness=False):
    reservation_url = "https://www.letskorail.com/ebizprd/EbizPrdTicketPr12111_i1.do"

    txtTotPsgCnt = adult + child + baby + senior + svrDsb + mldDsb
    body = {
        "txtSeatAttCd2": txtSeatAttCd_2,                            # 좌석방향 (000 - 기본, 009 - 순방향석, 010 - 역방향석)
        "txtSeatAttCd3": txtSeatAttCd_3,                            # 좌석위치 (000 - 기본, 011 - 1인석, 012 - 창측좌석, 013 - 내측좌석)
        "txtSeatAttCd4": txtSeatAttCd_4,                            # 좌석속성
        "txtTotPsgCnt": txtTotPsgCnt,                               # 총 인원 수
        "txtPsgTpCd1": "1",                                         # 고정값
        "txtPsgTpCd2": "3",                                         # 고정값
        "txtPsgTpCd3": "1",                                         # 고정값
        "txtPsgTpCd5": "1",                                         # 고정값
        "txtPsgTpCd7": "1",                                         # 고정값
        "txtPsgTpCd8": "3",                                         # 고정값
        "txtCompaCnt1": str(adult),                                 # 일반 어른
        "txtCompaCnt2": str(child),                                 # 일반 어린이
        "txtCompaCnt3": str(svrDsb),                                # 중증 장애인
        "txtCompaCnt5": str(senior),                                # 노인
        "txtCompaCnt7": str(mldDsb),                                # 경증 장애인
        "txtCompaCnt8": str(baby),                                  # 유아
        "txtJobId": "1102" if isReservation else "1101",            # 예약타입(1101 - 일반, 1102 - 예약, 1103 - 좌석선택)
        "txtJrnyCnt": "1",                                          # 여정 개수
        "txtPsrmClCd1": "2" if isBusiness else "1",                 # 좌석 등급 (1-일반실, 2-특실)
        "txtJrnySqno1": "001",                                      # 여정경로 (001 - 직통)
        "txtJrnyTpCd1": "11",                                       # 여정타입 (11 - 편도)
        "txtDptDt1": train_schedule['h_dpt_dt'],                    # 출발일 - 스케쥴 정보에 포함
        "txtDptRsStnCd1": train_schedule['h_dpt_rs_stn_cd'],        # 출발역 코드 - 스케쥴 정보에 포함
        "txtDptRsStnCdNm1": train_schedule['h_dpt_rs_stn_cd_nm'],   # 출발역 이름 - 스케쥴 정보에 포함
        "txtDptTm1": train_schedule['h_dpt_tm'],                    # 출발 시간 - 스케쥴 정보에 포함
        "txtArvRsStnCd1": train_schedule['h_arv_rs_stn_cd'],        # 도착역 코드 - 스케쥴 정보에 포함
        "txtArvRsStnCdNm1": train_schedule['h_arv_rs_stn_cd_nm'],   # 도착역 이름 - 스케쥴 정보에 포함
        "txtArvTm1": train_schedule['h_arv_tm'],                    # 열차 도착 시간 - 스케쥴 정보에 포함
        "txtTrnNo1": train_schedule['h_trn_no'],                    # 열차 번호 - 스케쥴 정보에 포함
        "txtRunDt1": train_schedule['h_run_dt'],                    # 출발일 - 스케쥴 정보에 포함
        "txtTrnClsfCd1": train_schedule['h_trn_clsf_cd'],           # 열차 종류 - 스케쥴 정보에 포함
        "txtTrnGpCd1": train_schedule['h_trn_gp_cd'],               # 기차 종류 - 스케쥴 정보에 포함
    }
    try:
        reservation_res = self.session.post(reservation_url, data=body, headers=self.get_req_headers(
            'https://www.letskorail.com/ebizprd/EbizPrdTicketPr21111_i1.do'))
    except Exception as e:
        self.error_callback('KTX 예매 실패', f"HTTP 요청에 실패했습니다 - \n{e}")
        return False

    try:
        if '로그아웃' not in reservation_res.text:
            if not self.login():
                self.error_callback('KTX 예매 실패', '예약 중 로그인 재시도 실패')  # 로그인 실패
                return False
            try:
                reservation_res = self.session.post(reservation_url, data=body, headers=self.get_req_headers(
                    'https://www.letskorail.com/ebizprd/EbizPrdTicketPr21111_i1.do'))
            except Exception as e:
                self.error_callback('KTX 예매 실패', f"예약 중 재로그인 후 HTTP 요청에 실패했습니다 - \n{e}")
                return False

    except Exception as e:
        self.error_callback('KTX 예매 실패', f"알 수 없는 에러 - \n{e}")
        return False

    detail_info = f"[{'특실' if isBusiness else '일반실'}] " \
                  f"{train_schedule['h_dpt_rs_stn_cd_nm']}⇀{train_schedule['h_arv_rs_stn_cd_nm']} " \
                  f"{train_schedule['h_dpt_tm'][0:2] + ':' + train_schedule['h_dpt_tm'][2:4]} " \
                  f"{'' if adult == 0 else '성인 ' + str(adult) + '명'} " \
                  f"{'' if child == 0 else '어린이 ' + str(child) + '명'} " \
                  f"{'' if baby == 0 else '아기 ' + str(baby) + '명'} " \
                  f"{'' if senior == 0 else '노인 ' + str(senior) + '명'} " \
                  f"{'' if svrDsb == 0 else '중증장애인 ' + str(svrDsb) + '명'} " \
                  f"{'' if mldDsb == 0 else '경증장애인 ' + str(mldDsb) + '명'} "

    if "20분 이내 결제" in reservation_res.text or "예약 대기" in reservation_res.text:
        self.try_callback(True, "", detail_info)
        return True
    if "잔여석없음" in reservation_res.text:
        self.try_callback(False, "잔여석 없음", detail_info)
    elif "예약대기자한도수초과" in reservation_res.text:
        self.try_callback(False, "예약대기자 한도수 초과", detail_info)
    elif "20분 이내 열차는 예약" in reservation_res.text:
        self.try_callback(False, "20분 이내 열차 예약 불가", detail_info)
    elif "일반최대 단체최소" in reservation_res.text:
        self.try_callback(False, "인원 수 오류, 9명 이하만 예약 가능", detail_info)
    else:
        self.try_callback(False, "기타 사유", detail_info + reservation_res.text)

    return False

 

 

풀 코드는 깃허브로..!