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