[Python] 파이썬으로 SRT 매진 표 예매하기 (+셀레늄)

개선된 내용의 게시물을 작성 중에 있습니다. 아래의 링크 참조 부탁드립니다. (2021/09/25)

파이썬으로 SRT 예매 프로그램 만들기 시리즈

https://kminito.tistory.com/79

 

[Python] 파이썬으로 SRT 예매 프로그램 만들기 (1) 기능 구현하기

안녕하세요? 과거에 처음 코딩을 배우던 시절 짠 허접한 SRT 매진 표 예매 게시물을 올렸었는데요, 이번에 코드를 새로 짜는 김에 파이썬 초보분들에게 도움이 될 수 있도록 어떤 식으로 접근하

kminito.tistory.com

 

 

 파이썬으로 SRT 표 예매 과정을 자동화 한 것입니다. 웹에서의 동작은 셀레늄을 이용했습니다. 아래 내용을 보시면 아시겠지만 단순히 사람이 하는 반복 작업을 파이썬을 통해 자동화 한 것이라 한계가 분명합니다.

 

 또한 해당 코드는 제가 Scope에 대한 개념이나 객체지향프로그래밍을 모를 때 작성한 이후 전혀 개선을 하지 않아, 코드가 조잡하고 비효율적이니 그냥 테스트 및 아이디어를 얻는 데에만 참조 부탁드립니다.

 

 

프로그램 개요

 SRT 홈페이지에서 특정 날짜의 특정 기차 번호 티켓을 예매 시도하고, 예매 성공하면 텔레그램으로 메시지를 보내줍니다. 만약 해당 티켓이 매진이면 몇초 후 새로고침하여 해당 작업을 반복합니다.

 

(텔레그램으로 메시지를 받는 이유는, 결제는 직접 해야 하기 때문입니다. 서버에 올려놓으면 휴대폰으로 원격으로 예매 요청보내고, 예매가 완료되어 메시지가 오면 SRT 어플을 켜서 결제합니다.)

 

 

알고리즘

 1) 크롬으로 SRT 홈페이지 로그인

 2) 날짜, 출발 시간, 출발지, 도착지 입력 후 기차 리스트 조회

 3) 조회 결과가 나오면 원하는 기차의 일반석이 예매 가능 상태인지 확인

 4-1) 예매 가능할 경우 예매 버튼 클릭 후 텔레그램 메시지 발송

 4-2) 예매 불가능할 경우 5~10초 후 새로고침, 다시 4-1) 반복

 

 

작동 영상 1

현재 일반석 매진인 동탄->동대구 2020.10.14 날짜의 311번 기차 예매 시도입니다.

계속 매진이라 몇초 간격으로 계속 새로고침 합니다.

 

 

 

 

작동 영상 2

현재 일반석 바로 예매 가능한 동탄->동대구 2020.10.15 날짜의 311번 기차 예매 시도입니다.

바로 예매됩니다. 

 

 

 

예매 완료되면 아래와 같이 텔레그램 메시지가 날아옵니다.

 

 

 

코드 

주의사항

 1) 홈페이지 로그인 정보 및 텔레그램 봇 정보는 수정하여 입력하세요.

 2) 과거에 저 혼자 쓰려고 대충 작성했던 것을 대충 사용하고 있으니, 코드 관련하여 조언은 정중히 사양하겠습니다.

 3) 질문은 올려주시면 제가 아는 선에서 성실하게 답변드릴 수 있도록 하겠습니다.

 

Selenium, Beautiful Soup 설치해서 사용해주세요.. 텔레그램 봇 사용법은 여기 (kminito.tistory.com/24)

 

# -*- coding: utf-8 -*-

import os
import sys
import datetime
from time import sleep
from random import randint
import requests
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.support.ui import Select

import telegram

bot = telegram.Bot(token='여기에 텔레그램 봇 토큰')
chat_id = '여기에 텔레그램 채팅방 아이디'
dpt_locs = {'동탄' :3 ,'동대구':9 , '울산':11, '수서':2, '천안아산':5,'부산':12}
avl_locs = {'동탄':21, '동대구':27, '수서':20, '울산':29, '부산':30, '천안아산':23}
time_table = {'6시':40, '16시':45}


def open_browser():
    options = webdriver.ChromeOptions()
    # options.add_argument('headless')
    options.add_argument('window-size=1920x1080')
    options.add_argument("disable-gpu")
    driver = webdriver.Chrome("chromedriver", chrome_options=options)
    return driver

def run():
    driver = open_browser()
    driver.get('https://etk.srail.co.kr/cmc/01/selectLoginForm.do')
    driver.implicitly_wait(15)
    driver.find_element_by_id('srchDvNm01').send_keys("SRT 회원번호")
    driver.find_element_by_id('hmpgPwdCphd01').send_keys("SRT 비밀번호")
    driver.find_element_by_xpath('//*[@id="login-form"]/fieldset/div[1]/div[1]/div[2]/div/div[2]/input').click()
    driver.implicitly_wait(4)
    sleep(0.4)
    driver.find_element_by_xpath('//*[@id="search-form"]/fieldset/div[1]/a').click()
    driver.implicitly_wait(1)
    driver.find_element_by_id("ui-id-{}".format(dpt_locs[dpt])).click()
    sleep(0.4)
    driver.find_element_by_xpath('//*[@id="search-form"]/fieldset/div[2]/a').click()
    driver.implicitly_wait(1)
    driver.find_element_by_id("ui-id-{}".format(avl_locs[avl])).click()
    date_ele = driver.find_element_by_xpath('//*[@id="search-form"]/fieldset/div[3]/div/input[1]')
    driver.execute_script("arguments[0].setAttribute('value','{}')".format(dpt_date), date_ele)
    driver.find_element_by_xpath('//*[@id="search-form"]/fieldset/div[4]/a').click()
    driver.find_element_by_id("ui-id-{}".format(time_table[when])).click()
    sleep(0.4)
    driver.find_element_by_xpath('//*[@id="search-form"]/fieldset/a').click()
    driver.implicitly_wait(4)
    sleep(0.4)

    counter = 1
    while True:
        soup = BeautifulSoup(driver.page_source, 'html.parser')
        trlist = soup.select('#result-form > fieldset > div.tbl_wrap.th_thead > table > tbody > tr')  # [tr,tr,tr,....]
        
        index=0
        done=False

        for tdl in trlist:
            if done == False:
                index += 1
                for train_no in train_nums:  
                    if tdl.select('.trnNo')[0].text.strip() == str(train_no):    
                        if '예약하기' in tdl.select("td")[6].find_all(text='예약하기'):
                            t = "#result-form > fieldset > div.tbl_wrap.th_thead > table > tbody > tr:nth-child("+str(index)+") > td:nth-child(7) > a"
                            driver.find_element_by_css_selector(t).click()
                            print("예약완료------------------------------------------------------------")
                            bot.sendMessage(chat_id=chat_id, text="예약완료!")
                            print("텔레그램 메시지 발송 완료--------------------------------------------")
                            done=True
                            break
                        else:
                            sleep(0.3)
            else:
                break


        print("loop done. counter : {}".format(counter))
        sleep(randint(5,10))
        
        if done == False:        
            elm = driver.find_element_by_xpath('//*[@id="search_top_tag"]/input')
            driver.execute_script("arguments[0].click();", elm)
            driver.implicitly_wait(2)    
            counter += 1
        else:
            driver.quit()
            break    

        if counter > 240:
            bot.sendMessage(chat_id=chat_id, text="240바퀴 돌았습니다. 자동 종료")
            driver.quit()
            break

if __name__ == "__main__":
    dpt = input("출발지 입력 (ex 동탄, 울산, 동대구, 수서, 천안아산) : ")
    avl = input("도착지 입력 - 출발지랑 마찬가지")
    dpt_date = input("날짜 입력. 반드시 2019.12.31 형태 : ")
    when = input("6시 or 16시 둘중 하나 : ")
    train_nums = list(map(int,input("기차 번호 입력 스페이스바로 구분 : ").strip().split()))   
    run()

 

추가

- 같은 방식으로 KTX도 가능합니다. 

- 나중에 시간이 나면 좀 깔끔하게 코드를 다듬어서 올리도록 하겠습니다.

- 텔레그램 봇 사용법 : kminito.tistory.com/24

 

[Python] 파이썬으로 텔레그램 봇 사용하기

텔레그램은 API를 제공하며, 파이썬에서 python-telegram-bot 라는 라이브러리를 이용하면 정말 간편하게 텔레그램 봇을 통하여 메시지를 발송할 수 있습니다. 프로그램 돌리면서, 휴대폰 텔레그램 앱

kminito.tistory.com

 

댓글

Designed by JB FACTORY