이제 예매하기 또는 열차표가 없을 경우 계속 좌석이 날때까지 새로고침을 하고 좌석이 나면 바로 예매하기를 하는 단계입니다. 하지만 이부분이 생각만큼 쉽지 않고 어려웠던 게 여러 생각치 못한 변수들을 대처하는 것이었습니다.

 

일단 10개의 열차편중에서 예매가 가능하든지 가능하지 않든지 하나를 선택해야 합니다. 1 ~ 10까지 하나의 열차편을 선택하면 되겠지요. 그래서 이번에도 정규표현식을 이용하여 예약하기'1~10중 원하는 열차 숫자'를 누르면 그 열차편을 예매하기 시도하는 것으로 진행해 보겠습니다.

 

1. telegram.py

1
2
3
4
5
6
7
        input_book = re.compile('예약하기[0-9]')         # 예약하기
        input_book_number = re.compile('[^예약하기]')           # 예약하기 빼기
 
        if input_book.match(tel_text):
            book_order_no = input_book_number.findall(tel_text)
            book_order_no = str(''.join(book_order_no))
            self.korail.try_seats(book_order_no)
cs

이번엔 긴 설명 안하고 예약하기'숫자'를 입력한 경우 korail.try_seats() 함수에게 숫자를 전달해 줍니다.

그리고 숫자는 꼭 str로 바꿔주는것도 잊지 맙시다.

 

2. korail.py

일단 전체 코드를 봅시다. (수많은 시행착오 후에 만들어진 코드입니다.ㅠㅠ ㅎㅎ)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    def try_seats(self, order):
        from tele.telegram import Telegram
        count = 0
        unavailable_seats = None
        while True:
            if unavailable_seats == "예약하기":
                self.driver.find_element_by_xpath("/html/body/div[1]/div[3]/div/div[1]/form[1]/div/div[4]/table[1]/tbody/tr[%s]/td[6]//img" % order).click()
                time.sleep(3)
                Telegram.start_telegram(self, {'from':{'id':1051549721},'text':'예약완료'})
                try:
                    popup_iframe = self.driver.find_element_by_xpath('//*[@id="embeded-modal-traininfo"]')
                    self.driver.switch_to_frame(popup_iframe)
                    self.driver.find_element_by_link_text('닫기').click()
                    time.sleep(1)
                finally:
                    time.sleep(3)
                    alert = self.driver.switch_to.alert
                    alert.accept()
                    time.sleep(1)
            else:
                unavailable_seats = self.driver.find_element_by_xpath("/html/body/div[1]/div[3]/div/div[1]/form[1]/div/div[4]/table[1]/tbody/tr[%s]/td[6]//img" % order).get_attribute('alt')
                self.driver.find_element_by_css_selector(".btn_inq > a:nth-child(1) > img:nth-child(1)").click()
                time.sleep(2.5)
                count += 1
                if count%10 == 0:
                    Telegram.start_telegram(self, {'from':{'id': 내 chat id 번호},'text':'예약중'})
                print(count)
cs

from tele.telegram import Telegram

: 이미 설명했듯이 순환참조를 막기 위하여 지금 import합니다. 앞서 함수에서 불러온 것은 날라갔겠지요?

 

count = 0

: 이 코드는 나중에 예매 불가 상태일때 몇번 예매 시도를 했는지 카운트 하기 위한 목적입니다.

 

unavailable_seats = None

: 내가 원하는 열차편의 예매가능 여부 확인입니다. 일단 None으로 지정해 놓습니다.

 

While True:

: 그리고 다음줄부터는 While문으로 무한루프를 시켜줍니다. 예매가 될 때까지 계속 진행해야 하니깐요.

 

일단 예매 표가 없을때 계속 시도해야하기 때문에 else: 구문부터 보는것이 나을듯 합니다.

else:

    unavailable_seats = self.driver.find_element_by_xpath("/html......[%s]/td[6]//img" % order).get_attribute('alt')

일반실 아래에 있는 박스안의 예매 그림파일을 불러옵니다.

위의 일반실 아래 예매 버튼 그림파일의 설명(alt)이 '예매하기'일 때 예매하기가 가능합니다.

 

self.driver.find_element_by_css_selector(".btn.......................").click()

time.sleep(2.5)

 

그리고 이 구문은 예매 불가일 경우이기 때문에 새로고침기능이 있는 조회하기 버튼을 눌러 다시 조회를 하고 2.5초를 기다려줍니다.(창이 다 떠야 하니깐요.)

 

컴퓨터/인터넷 성능이 좋다면 저 위의 시간을 단축하셔도 됩니다! 저는 노트북으로 작업하고 있어서 안정적인 진행을 위하여 넉넉하게 잡았습니다.

 

count += 1

if count%10 == 0:

    Telegram.start_telegram(self, {'chat':{'id': 내 챗 id 번호}, 'text':'예약중'})

print(count)

 

예매하기를 시도하고 실제 컴퓨터가 예매를 시도를 하는지 그냥 예상치 못한 변수로 중지되어 버렸는지 알 수가 없습니다. 그렇기 때문에 일정한 간격으로 저에게 예약중, 즉 예약 시도중이라는 메세지를 보내주도록 합니다. 10회 시도하면 저에게 메세지를 보내도록 했는데 너무 자주오네요.ㅎ

 

그리고 위 구문은 아래 telegram.py 안의 코드와 연결됩니다.

1
2
3
4
        if tel_text == "예약중":
            self.token = "내 텔레그램 챗봇 토큰 번호"
            self.bot = telepot.Bot(self.token)
            self.bot.sendMessage(chat_id, "예약시도중입니다.")
cs

예약시도중입니다 메세지가 계속 텔레그램으로 전달됩니다.

 

이제 본격적으로 예약을 했을때의 과정을 봅시다. 하지만 여기서 여러 경우의 수를 먼저 생각하고 코드를 보는것이 나을 것 같습니다.

 

1. 좌석이 났지만 내가 예매하기를 누르기 직전에 다른사람이 또 예매하기를 했을 경우.

   (사람이든 또 다른 매크로든지 간에 이러한 경우는 충분히 있을수 있습니다.)

2. 기본적으로 링크를 클릭하는 순간 예매가 완료되지만 ktx-산천의 경우 예매버튼을 누르고 아래 화면이 뜨는경우 확인을 다시 한번 더 눌러야 예매가 완료됩니다.

위 창을 닫거나 예매 계속 진행하기 버튼을 눌러야 예매가 됩니다.

if unavailable_seats == "예약하기":

    self.driver.find_element_by_xpath("/html......[%s]....." % order).click()

    time.sleep(3)

    Telegram.start_telegram(self, {'chat':{'id': 내 chat id 숫자}, 'text':'예약완료'})

 

: 예매가 가능하기 때문에 바로 click()을 이용하예 예매를 하고 3초 후에 텔레그램 챗봇을 통해 예약완료 메세지를 보냅니다. 

 

그리고 아래 try: 구문의 경우 ktx-산천편의 경우입니다. 팝업창을 닫는 작업입니다. 이경우 ktx-산천편에 해당하는 경우에만 팝업창이 뜨기 때문에 try 구문을 썼습니다.

try:

    popup_iframe = self.driver.find_element_by_xpath('//*[@id="embeded-modal-traininfo"]')

    self.driver.switch_to_frame(popup_iframe)

    self.driver.find_element_by_link_text('닫기').click()

    time.sleep(1)

: 짐작하시듯이 팝업창의 경우 alert가 아닌 iframe으로 별도 프레임창이 생성되고 창이 뜨기 때문에 위와 같이 작성합니다.

 

finally:

    time.sleep(3)

    alert = self.driver.switch_to.alert

    alert.accept()

    time.sleep(1)

그리고 마지막 구문의 경우 finally: 구문을 사용하여 어느경우라도 예매를 성공했을 때 뜨는 아래 창을 닫아줍니다. 아래 화면에는 두번 연속 창이 뜨지만 첫번째 창의 경우에는 별도 클릭을 안해도 앞에 time.sleep(3) 하는동안 사라집니다. 

(이부분은 직접 내가 .accept() 해줄수도 있는데 그런경우 ktx-산천 편의 경우 로그인이 팅겨버리는 현상이 발생하여 그냥 time.sleep(3)을 주었으니 이점 참고하시기 바랍니다.)

 

이 창은 알아서 넘어갑니다.(이유는 잘 모르겠습니다.
이 창은 직접 확인을 눌러줍니다.

그리고 앞서 예약을 하였을 경우 telegram.py에 예매 완료했다고 메세지를 보내줘야겠지요.

1
2
3
4
        if tel_text == "예약완료":
            self.token = "내 텔레그램 챗봇 대화방 토큰 번호"
            self.bot = telepot.Bot(self.token)
            self.bot.sendMessage(chat_id, "예약완료하였습니다. 앱을 통해 확인해보세요.")
cs

예약 완료하였습니다. 앱을 통해 확인해보세요. 라는 문구를 괜히 넣은것이 아니라 실제 앞에서 언급한 경우의 수 중에 예매를 누르려는 찰나 다른 이가 낚아챘을 가능성이 있기 때문에 꼭 확인을 해보고 실패하였을 경우 다시 진행해야 합니다.

 

이제 마지막 포스트면 마치겠네요. ;)

목차

Python, Telegram으로 KTX 열차표 예매하기(#1 준비)
Python, Telegram으로 KTX 열차표 예매하기(#2 __init__.py)
Python, Telegram으로 KTX 열차표 예매하기(#3 telegram.py, korail.py)
Python, Telegram으로 KTX 열차표 예매하기(#4 텔레그램봇 만들기)

Python, Telegram으로 KTX 열차표 예매하기(#5 telegram, telepot)
Python, Telegram으로 KTX 열차표 예매하기(#6 selenium, 웹 자동화)
Python, Telegram으로 KTX 열차표 예매하기(#7 selenium, regular expression)
Python, Telegram으로 KTX 열차표 예매하기(#8 열차표 확인하기)
Python, Telegram으로 KTX 열차표 예매하기(#9 예매하기)
Python, Telegram으로 KTX 열차표 예매하기(#10 마무리)

+ Recent posts