본문 바로가기
그냥 끄적끄적

인터파크 매크로 만들기 [4. 좌석 선점하기 - 마무리]

by 강성주의 알고리즘 2023. 12. 1.

2023.11.29 - [그냥 끄적끄적] - 인터파크 매크로 만들기. [1. 환경 구축]

2023.11.29 - [그냥 끄적끄적] - 인터파크 매크로 만들기. [2. 로그인]

2023.11.30 - [그냥 끄적끄적] - 인터파크 매크로 만들기 [3. 공연 스케쥴 선택하기 - 1]

2023.11.30 - [그냥 끄적끄적] - 인터파크 매크로 만들기 [3. 공연 스케쥴 선택하기 - 2]

2023.12.01 - [그냥 끄적끄적] - 인터파크 매크로 만들기 [4. 좌석 선점하기 - 1]

이전 포스팅을 보고 오시면 이해가 쉽습니다. 해당 포스팅들은 ChatGPT만 사용하여도 충분히 구현할 수 있는 매크로를 구현할 수 있음을 강조하기 위해 작성하였습니다. 


저번 포스팅에서는 2, 3층 좌석을 선택하기 위해 층 이동까지 했으니, 이번 포스팅에서는 좌석을 선택하고 결제창까지 넘어가 보도록 하겠습니다.

일단 실행 영상부터 보시죠.  1월 13일 7시 (2회차) 공연의 3층 3 연석을 잡는 시나리오에 대한 영상입니다.

최종 구동 영상

 


이전 포스팅까지 잘 따라오셨다면 2, 3층으로 화면을 전환했으니 좌석을 선택할 수 있는 프레임으로 다시 driver를 이동시켜야 합니다.

이전에 티켓팅을 진행하는 새창에 대한 정보를 저장했던 new_window_handle로 다시 driver를 옮긴 후, 좌석을 선택할 수 있는 iframe으로 driver를 이동해야 합니다. 복잡하지만 어쩔 수 없습니다..

driver.switch_to.window(new_window_handle)
iframes = driver.find_elements(By.TAG_NAME, "iframe")

아래 경로를 보시면 좌석을 선택할 수 있는 element 들은 iframe#ifrmSeat 아래.. iframe#ifrmSeatDetail 이라는 프레임에 속해있는 것을 알 수 있습니다. 아래 코드로 해당 프레임으로 driver를 이동시킬 수 있습니다.

for ifr in iframes:
    if ifr.get_attribute('name') == 'ifrmSeat':
        driver.switch_to.frame(ifr)
        driver.switch_to.frame('ifrmSeatDetail')
        break

그럼 이제 선택 가능한 좌석 정보들을 모두 받아오기위해 아래의 코드를 실행하면 됩니다.

available_reserve_seat = driver.find_elements(By.XPATH, "//img[@class='stySeat']")

이제는 available_reserve_seat 변수에 우리가 선택가능한 모든 좌석의 정보들이 들어 있습니다. 좌석 정보는 [ 등급, 층-열-번호]를 담고 있으며 우리는 여기서 층, 열, 번호를 기반으로 연석을 잡을 수 있게 되는 것입니다.

일단 연석이라하면 같은 열이겠죠? 모든 좌석을 층과 열에 따라 분류하고 원하는 층에서 원하는 연석의 수만큼 좌석이 있는 열에서 연석을 선택할 수 있습니다. 하나하나 설명하면 복잡하니 아래 코드를 사용합니다. 최대한 복잡하지 않으면서 반복문을 적게 돌 수 있는 코드로 작성을 해 보았습니다.

seat_map = [[[] for i in range(25)] for j in range(4)]
seat_map_num = [[[] for i in range(25)] for j in range(4)]
for seat in available_reserve_seat:
    _str = str(seat.get_attribute('alt')).split(' ')
    seat_floor = int(_str[1].split('-')[0].split('층')[0])
    if seat_floor == want_floor:
        seat_row = int(_str[1].split('-')[1].split('열')[0])
        seat_column = int(_str[1].split('-')[2])
        seat_map[seat_floor][seat_row].append(seat.get_attribute('alt'))
        seat_map_num[seat_floor][seat_row].append(seat_column)

for row in range(25): # 열
    _size = len(seat_map[want_floor][row])
    if _size >= want_seat_cnt: # 해당 층의 열에 원하는 연석 자리수 이상있어야함
        for i in range(0, _size - want_seat_cnt):
            ret = 0 # 현재 열에서 k 자리 연석이 있냐?
            pre_num = seat_map_num[want_floor][row][i] - 1
            for k in range(want_seat_cnt):
                if seat_map_num[want_floor][row][i+k] == pre_num + 1:
                    ret += 1
                    pre_num += 1
                else:
                    break

        if ret == want_seat_cnt:
            for k in range(want_seat_cnt):
                want_seat_list.append(seat_map[want_floor][row][i+k])
            flag = 1
            break;
    if flag == 1:
        break

 

아니야 나는 연석은 필요 없고 그냥 내가 원하는 자리 수만큼만 티켓팅을 하고 싶어! 한다면 아래 코드를  사용하면 됩니다.

ret = 0
for seat in available_reserve_seat:
    want_seat_list.append(seat)
    ret = ret + 1
    if ret == want_seat_cnt:
        flag = 1
        break

 

둘을 합치면 아래와 같겠네요

# 먹을수있는 자리를 먹어라
want_seat_cnt = 3 # 자리수
want_continue = 1 # 1 = 연석, 0 = 연석 상관 없음

flag = 0
want_seat_list = []
if want_continue == 1: # 연석 자리 잡기

    seat_map = [[[] for i in range(25)] for j in range(4)]
    seat_map_num = [[[] for i in range(25)] for j in range(4)]
    for seat in available_reserve_seat:
        _str = str(seat.get_attribute('alt')).split(' ')
        seat_floor = int(_str[1].split('-')[0].split('층')[0])
        if seat_floor == want_floor:
            seat_row = int(_str[1].split('-')[1].split('열')[0])
            seat_column = int(_str[1].split('-')[2])
            seat_map[seat_floor][seat_row].append(seat.get_attribute('alt'))
            seat_map_num[seat_floor][seat_row].append(seat_column)

    for row in range(25): # 열
        _size = len(seat_map[want_floor][row])
        if _size >= want_seat_cnt: # 해당 층의 열에 원하는 연석 자리수 이상있어야함
            for i in range(0, _size - want_seat_cnt):
                ret = 0 # 현재 열에서 k 자리 연석이 있냐?
                pre_num = seat_map_num[want_floor][row][i] - 1
                for k in range(want_seat_cnt):
                    if seat_map_num[want_floor][row][i+k] == pre_num + 1:
                        ret += 1
                        pre_num += 1
                    else:
                        break

            if ret == want_seat_cnt:
                for k in range(want_seat_cnt):
                    want_seat_list.append(seat_map[want_floor][row][i+k])
                flag = 1
                break;
        if flag == 1:
            break

else:
    ret = 0
    for seat in available_reserve_seat:
        want_seat_list.append(seat)
        ret = ret + 1
        if ret == want_seat_cnt:
            flag = 1
            break

우리가 원하는 좌석들은 조건에 따라서 want_seat_list 배열에 저장될 것이고, 선택이 완료된다면 해당 좌석 정보의 element를 click 해서 좌석을 선택한 후 결제페이지로 넘어가면 됩니다

아래 코드는 해당 기능들이 모두 포함된 거의 최종 코드이며, 현 상태로 매크로를 실행해도 무방합니다. 실행 영상은 본 포스팅의 맨 앞 영상입니다.

import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By

my_interpark_id = "인터파크 아이디"
my_interpark_pw = "인터파크 비밀번호"

login_url = "https://ticket.interpark.com/Gate/TPLogin.asp" # 로그인 창
my_url = "https://tickets.interpark.com/goods/L0000062" # 공연 URL

want_month = 1 # 원하는 월
want_day = 13 # 원하는 일
want_turn = 2 # 원하는 공연 회차 ( 1회 = 2시 , 2회 = 7 시)
want_floor = 3 # 원하는 관람 층 (1 = 1층, 2 = 2층, 3 = 3층)

service = Service('C:/Users/seong/chromedriver.exe')
service.start()

driver = webdriver.Remote(service.service_url)

driver.get(login_url)

iframes = driver.find_elements(By.TAG_NAME, "iframe")
driver.switch_to.frame(iframes[0])

time.sleep(0.2)
id_input = driver.find_element(By.CSS_SELECTOR, "#userId")
pw_input = driver.find_element(By.CSS_SELECTOR, "#userPwd")
id_input.send_keys(my_interpark_id)
time.sleep(0.2)
pw_input.send_keys(my_interpark_pw)
button = driver.find_element(By.CSS_SELECTOR, "#btn_login")
button.click()

driver.get(my_url)
time.sleep(0.3)

nxt_button = driver.find_element(By.XPATH, "//*[@id='productSide']/div/div[1]/div[1]/div[2]/div/div/div/div/ul[1]/li[3]")

while True:
    current_date = driver.find_element(By.XPATH,"//*[@id='productSide']/div/div[1]/div[1]/div[2]/div/div/div/div/ul[1]/li[2]")
    cur_month = int(current_date.text.split(' ')[1])
    if cur_month != want_month:
        nxt_button.click()
        time.sleep(0.3)
    else:
        time.sleep(0.3)
        break

find_day = driver.find_element(By.XPATH, "//li[text()='"+str(want_day)+"']")
find_day.click()

turn_list = driver.find_elements(By.CLASS_NAME, "timeTableItem")
turn_list[want_turn - 1].click()

go_button = driver.find_element(By.CSS_SELECTOR, "a.sideBtn.is-primary")
go_button.click()

time.sleep(1)

main_window_handle = driver.current_window_handle
all_window_handles = driver.window_handles
new_window_handle = [handle for handle in all_window_handles if handle != main_window_handle][0]
# 새 창으로 전환
driver.switch_to.window(new_window_handle)

iframes = driver.find_elements(By.TAG_NAME, "iframe")
for ifr in iframes:
    if ifr.get_attribute('name') == 'ifrmSeat':
        print("convert")
        driver.switch_to.frame(ifr)
        driver.switch_to.frame('ifrmSeatView')
        break

if want_floor != 1: # 2 or 3층
    Seat_region = driver.find_element(By.XPATH, "//*[@id='TmgsTable']/tbody/tr/td/map")
    area_element = Seat_region.find_element(By.TAG_NAME, "area")

    href_value = area_element.get_attribute("href")
    # href 값이 javascript:로 시작하는지 확인
    if href_value.startswith("javascript:"):
        driver.execute_script(href_value)
    time.sleep(0.5)

driver.switch_to.window(new_window_handle)
iframes = driver.find_elements(By.TAG_NAME, "iframe")

for ifr in iframes:
    if ifr.get_attribute('name') == 'ifrmSeat':
        driver.switch_to.frame(ifr)
        driver.switch_to.frame('ifrmSeatDetail')
        break

available_reserve_seat = driver.find_elements(By.XPATH, "//img[@class='stySeat']")

# 먹을수있는 자리를 먹어라
want_seat_cnt = 3 # 자리수
want_continue = 1 # 1 = 연석, 0 = 연석 상관 없음

flag = 0
want_seat_list = []
if want_continue == 1: # 연석 자리 잡기

    seat_map = [[[] for i in range(25)] for j in range(4)]
    seat_map_num = [[[] for i in range(25)] for j in range(4)]
    for seat in available_reserve_seat:
        _str = str(seat.get_attribute('alt')).split(' ')
        seat_floor = int(_str[1].split('-')[0].split('층')[0])
        if seat_floor == want_floor:
            seat_row = int(_str[1].split('-')[1].split('열')[0])
            seat_column = int(_str[1].split('-')[2])
            seat_map[seat_floor][seat_row].append(seat.get_attribute('alt'))
            seat_map_num[seat_floor][seat_row].append(seat_column)

    for row in range(25): # 열
        _size = len(seat_map[want_floor][row])
        if _size >= want_seat_cnt: # 해당 층의 열에 원하는 연석 자리수 이상있어야함
            for i in range(0, _size - want_seat_cnt):
                ret = 0 # 현재 열에서 k 자리 연석이 있냐?
                pre_num = seat_map_num[want_floor][row][i] - 1
                for k in range(want_seat_cnt):
                    if seat_map_num[want_floor][row][i+k] == pre_num + 1:
                        ret += 1
                        pre_num += 1
                    else:
                        break

            if ret == want_seat_cnt:
                for k in range(want_seat_cnt):
                    want_seat_list.append(seat_map[want_floor][row][i+k])
                flag = 1
                break;
        if flag == 1:
            break

else:
    ret = 0
    for seat in available_reserve_seat:
        want_seat_list.append(seat)
        ret = ret + 1
        if ret == want_seat_cnt:
            flag = 1
            break

if flag == 1:
    for str in want_seat_list:
        b1 = driver.find_element(By.XPATH,"//img[@alt='"+str+"']")
        b1.click()

    driver.switch_to.window(new_window_handle)
    iframes = driver.find_elements(By.TAG_NAME, "iframe")

    for ifr in iframes:
        if ifr.get_attribute('name') == 'ifrmSeat':
            driver.switch_to.frame(ifr)
            break

    select_button = driver.find_element(By.XPATH, "//*[@id='NextStepImage']")
    select_button.click()

time.sleep(10)
driver.switch_to.default_content()
driver.quit()

매크로는 빠르게 선택하는 도구일 뿐, 원하는 시간, 좌석에 대한 전략을 수립하는건 오로지 본인의 몫입니다. 딜레이를 조절하고, 티켓팅 시간 이전 언제 실행할 것이며, 전체 동작흐름은 어떻게 설계할 것인지 고민해보고 자신의 환경에 맞게 코드를 수정하는 것은 자유입니다.

질문은 댓글로 남겨주시기 바라며, 제 매크로가 암표 장사에 악용되지 않았으면 합니다. (뭐 암표로 돈 버는 사람들은 더 좋은 매크로를 쓰겠지만..)

지금까지 ChatGPT 및 몇몇 웹 관련 지식들을 잘 조합해서 만든 매크로였습니다. 감사합니다.

반응형