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

SRT&KTX 기차표 매크로 예매 - (3) SRT 승차권 조회

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

예매하고자 하는 승차권을 정확하게 알고 있다면, 굳이 승차권 리스트를 조회할 필요가 없다.

 

그래도 최종적으로 UI도 입히고, 유저 편의성을 높이려면 조회 기능은 필요할 것 같아 구현해보려고 한다.

 

결론적으로 말하자면, SRT 승차권 리스트를 Json 형태로 받아오는 방법을 못찾았다...

 

HTML 코드를 받아서 파싱을 해야한다. 이것만은 하기 싫었는데...

 

승차권 조회를 여러번 해보면서 찾은 결과
  • URL : https://etk.srail.kr/hpg/hra/01/selectScheduleList.do 
  • Method : POST
  • Query : pageId=TK0101010000 (없어도 되는 것으로 추정됨)

Data Field

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

승차권 조회 POST request

위 정보를 가지고 Post 요청을 보내는 코드를 짜보자.

def fetch_schedule(self, dptRsStnCd, arvRsStnCd, 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": dptRsStnCd,                           # 출발역 코드 (e.g. 0551)
        "arvRsStnCd": arvRsStnCd,                           # 도착역 코드
        "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)

    print(res.text)

위 코드를 실행해보면, 시간표 정보가 포함된 HTML 코드가 잘 수신되는걸 확인할 수 있다.

 

변수를 조금씩 바꿔가며 테스트해보니, 기차역 이름은 중요하지 않은것 같다. 역 코드를 기준으로 조회하는 듯 싶다.

 

여기서 유의미한 시간표 정보를 뽑아보자.

 

크롬 개발자 도구로 소스를 만지다 보니, "trnNo" class 안에 해당 기차의 정보가 매우 예쁘게 정리되어 있는 것을 확인할 수 있다.

기차 정보가 담긴 element

BeautifulSoup를 이용하여 기차 정보를 뽑아보자.

import requests
from bs4 import BeautifulSoup


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

 

각 필드 값의 의미를 몰라도 된다. 나중에 예매할 때 필요한 정보만 사용하면 되니까.

 

srt.fetch_schedule('0297', '0552', '오송', '동탄', '20240110', '193100', 1, 0, 0, 0, 0)

위 샘플코드를 돌려보면 결과가 아래처럼 잘 나온다

[{'trnOrdrNo': '0', 'jrnySqno': '001', 'runDt': '20240110', 'trnNo': '00384', 'trnGpCd': '300', 'stlbTrnClsfCd': '17', 'dptDt': '20240110', 'dptTm': '193100', 'dptRsStnCd': '0297', 'dptRsStnCdNm': '오송', 'dptStnConsOrdr': '000006', 'dptStnRunOrdr': '000009', 'arvRsStnCd': '0552', 'arvRsStnCdNm': '동탄', 'arvStnConsOrdr': '000002', 'arvStnRunOrdr': '000012', 'seatAttCd': '015', 'scarGridcnt': '', 'scarNo': '', 'seatNo_1': '', 'seatNo_2': '', 'seatNo_3': '', 'seatNo_4': '', 'seatNo_5': '', 'seatNo_6': '', 'seatNo_7': '', 'seatNo_8': '', 'seatNo_9': '', 'trainDiscGenRt': '0.0', 'rcvdAmt': '10700', 'rcvdFare': '4800', 'trnNstpLeadInfo': ''}, {'trnOrdrNo': '1', 'jrnySqno': '001', 'runDt': '20240110', 'trnNo': '00618', 'trnGpCd': '300', 'stlbTrnClsfCd': '17', 'dptDt': '20240110', 'dptTm': '194000', 'dptRsStnCd': '0297', 'dptRsStnCdNm': '오송', 'dptStnConsOrdr': '000006', 'dptStnRunOrdr': '000005', 'arvRsStnCd': '0552', 'arvRsStnCdNm': '동탄', 'arvStnConsOrdr': '000002', 'arvStnRunOrdr': '000008', 'seatAttCd': '015', 'scarGridcnt': '', 'scarNo': '', 'seatNo_1': '', 'seatNo_2': '', 'seatNo_3': '', 'seatNo_4': '', 'seatNo_5': '', 'seatNo_6': '', 'seatNo_7': '', 'seatNo_8': '', 'seatNo_9': '', 'trainDiscGenRt': '0.0', 'rcvdAmt': '10700', 'rcvdFare': '4800', 'trnNstpLeadInfo': ''}, {'trnOrdrNo': '2', 'jrnySqno': '001', 'runDt': '20240110', 'trnNo': '00362', 'trnGpCd': '300', 'stlbTrnClsfCd': '17', 'dptDt': '20240110', 'dptTm': '204600', 'dptRsStnCd': '0297', 'dptRsStnCdNm': '오송', 'dptStnConsOrdr': '000006', 'dptStnRunOrdr': '000006', 'arvRsStnCd': '0552', 'arvRsStnCdNm': '동탄', 'arvStnConsOrdr': '000002', 'arvStnRunOrdr': '000007', 'seatAttCd': '015', 'scarGridcnt': '', 'scarNo': '', 'seatNo_1': '', 'seatNo_2': '', 'seatNo_3': '', 'seatNo_4': '', 'seatNo_5': '', 'seatNo_6': '', 'seatNo_7': '', 'seatNo_8': '', 'seatNo_9': '', 'trainDiscGenRt': '0.0', 'rcvdAmt': '10700', 'rcvdFare': '4800', 'trnNstpLeadInfo': ''}, {'trnOrdrNo': '3', 'jrnySqno': '001', 'runDt': '20240110', 'trnNo': '00664', 'trnGpCd': '300', 'stlbTrnClsfCd': '17', 'dptDt': '20240110', 'dptTm': '205500', 'dptRsStnCd': '0297', 'dptRsStnCdNm': '오송', 'dptStnConsOrdr': '000006', 'dptStnRunOrdr': '000007', 'arvRsStnCd': '0552', 'arvRsStnCdNm': '동탄', 'arvStnConsOrdr': '000002', 'arvStnRunOrdr': '000009', 'seatAttCd': '015', 'scarGridcnt': '', 'scarNo': '', 'seatNo_1': '', 'seatNo_2': '', 'seatNo_3': '', 'seatNo_4': '', 'seatNo_5': '', 'seatNo_6': '', 'seatNo_7': '', 'seatNo_8': '', 'seatNo_9': '', 'trainDiscGenRt': '0.0', 'rcvdAmt': '10700', 'rcvdFare': '4800', 'trnNstpLeadInfo': ''}, {'trnOrdrNo': '4', 'jrnySqno': '001', 'runDt': '20240110', 'trnNo': '00620', 'trnGpCd': '300', 'stlbTrnClsfCd': '17', 'dptDt': '20240110', 'dptTm': '213000', 'dptRsStnCd': '0297', 'dptRsStnCdNm': '오송', 'dptStnConsOrdr': '000006', 'dptStnRunOrdr': '000004', 'arvRsStnCd': '0552', 'arvRsStnCdNm': '동탄', 'arvStnConsOrdr': '000002', 'arvStnRunOrdr': '000006', 'seatAttCd': '015', 'scarGridcnt': '', 'scarNo': '', 'seatNo_1': '', 'seatNo_2': '', 'seatNo_3': '', 'seatNo_4': '', 'seatNo_5': '', 'seatNo_6': '', 'seatNo_7': '', 'seatNo_8': '', 'seatNo_9': '', 'trainDiscGenRt': '0.0', 'rcvdAmt': '10700', 'rcvdFare': '4800', 'trnNstpLeadInfo': ''}, {'trnOrdrNo': '5', 'jrnySqno': '001', 'runDt': '20240110', 'trnNo': '00366', 'trnGpCd': '300', 'stlbTrnClsfCd': '17', 'dptDt': '20240110', 'dptTm': '214000', 'dptRsStnCd': '0297', 'dptRsStnCdNm': '오송', 'dptStnConsOrdr': '000006', 'dptStnRunOrdr': '000004', 'arvRsStnCd': '0552', 'arvRsStnCdNm': '동탄', 'arvStnConsOrdr': '000002', 'arvStnRunOrdr': '000006', 'seatAttCd': '015', 'scarGridcnt': '', 'scarNo': '', 'seatNo_1': '', 'seatNo_2': '', 'seatNo_3': '', 'seatNo_4': '', 'seatNo_5': '', 'seatNo_6': '', 'seatNo_7': '', 'seatNo_8': '', 'seatNo_9': '', 'trainDiscGenRt': '0.0', 'rcvdAmt': '10700', 'rcvdFare': '4800', 'trnNstpLeadInfo': ''}, {'trnOrdrNo': '6', 'jrnySqno': '001', 'runDt': '20240110', 'trnNo': '00666', 'trnGpCd': '300', 'stlbTrnClsfCd': '17', 'dptDt': '20240110', 'dptTm': '221500', 'dptRsStnCd': '0297', 'dptRsStnCdNm': '오송', 'dptStnConsOrdr': '000006', 'dptStnRunOrdr': '000007', 'arvRsStnCd': '0552', 'arvRsStnCdNm': '동탄', 'arvStnConsOrdr': '000002', 'arvStnRunOrdr': '000009', 'seatAttCd': '015', 'scarGridcnt': '', 'scarNo': '', 'seatNo_1': '', 'seatNo_2': '', 'seatNo_3': '', 'seatNo_4': '', 'seatNo_5': '', 'seatNo_6': '', 'seatNo_7': '', 'seatNo_8': '', 'seatNo_9': '', 'trainDiscGenRt': '0.0', 'rcvdAmt': '10700', 'rcvdFare': '4800', 'trnNstpLeadInfo': ''}, {'trnOrdrNo': '7', 'jrnySqno': '001', 'runDt': '20240110', 'trnNo': '00370', 'trnGpCd': '300', 'stlbTrnClsfCd': '17', 'dptDt': '20240110', 'dptTm': '222400', 'dptRsStnCd': '0297', 'dptRsStnCdNm': '오송', 'dptStnConsOrdr': '000006', 'dptStnRunOrdr': '000006', 'arvRsStnCd': '0552', 'arvRsStnCdNm': '동탄', 'arvStnConsOrdr': '000002', 'arvStnRunOrdr': '000008', 'seatAttCd': '015', 'scarGridcnt': '', 'scarNo': '', 'seatNo_1': '', 'seatNo_2': '', 'seatNo_3': '', 'seatNo_4': '', 'seatNo_5': '', 'seatNo_6': '', 'seatNo_7': '', 'seatNo_8': '', 'seatNo_9': '', 'trainDiscGenRt': '0.0', 'rcvdAmt': '10700', 'rcvdFare': '4800', 'trnNstpLeadInfo': ''}, {'trnOrdrNo': '8', 'jrnySqno': '001', 'runDt': '20240110', 'trnNo': '00376', 'trnGpCd': '300', 'stlbTrnClsfCd': '17', 'dptDt': '20240110', 'dptTm': '233700', 'dptRsStnCd': '0297', 'dptRsStnCdNm': '오송', 'dptStnConsOrdr': '000006', 'dptStnRunOrdr': '000005', 'arvRsStnCd': '0552', 'arvRsStnCdNm': '동탄', 'arvStnConsOrdr': '000002', 'arvStnRunOrdr': '000006', 'seatAttCd': '015', 'scarGridcnt': '', 'scarNo': '', 'seatNo_1': '', 'seatNo_2': '', 'seatNo_3': '', 'seatNo_4': '', 'seatNo_5': '', 'seatNo_6': '', 'seatNo_7': '', 'seatNo_8': '', 'seatNo_9': '', 'trainDiscGenRt': '0.0', 'rcvdAmt': '10700', 'rcvdFare': '4800', 'trnNstpLeadInfo': ''}]

 

기차역 코드를 자동으로 가져오자

여기서 끝내려고 했으나... 아무래도 기차역 리스트와 코드를 자동으로 가져와야 할 것 같다.

 

유저 입장에서 기차역 코드를 알 필요가 전혀 없다. 사람은 기차역 이름을 보고 예매하지, 코드를 보지 않는다.

 

그리고 나중에 기차역이 새로 지어지거나 추가된다면... 유지보수하기가 상당히 귀찮을테니 자동으로 리스트를 가져와보자.

 

뭔가 정보를 얻을 수 있을 것 같은 버튼

승차권 조회 페이지 코드를 살펴봤으나, 기차역 리스트와 역코드가 담긴 정보는 없었다.

 

출발역/도착역을 바꿀 수 있는 버튼이 수상스러우니 클릭해보자.

기차역 코드 확인

소스에서 기차역 이름과 코드를 확인할 수 있다.

 

가져오자.

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

 

역 정보는 class 선언할 때 불러줘도 될 것 같으니, 수정하면 완성된 코드는 아래와 같다!


import re
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


if __name__ == "__main__":
    srt = SRT('1', '', '')
    srt.login()
    srt.is_logged_in()
    schedules = srt.fetch_schedule('오송', '동탄', '20240110', '193100', 1, 0, 0, 0, 0)

    print(schedules)