├── README.md └── macro.py /README.md: -------------------------------------------------------------------------------- 1 | [Python - 인터파크 티켓팅 매크로 구현](https://codechacha.com/ko/selenium-interpark-ticket-macro/)에서 소개된 코드입니다. 2 | 3 | ## Reference 4 | * [GitHub - Interpark_Macro](https://github.com/InterMacro/Interpark_Macro) 5 | -------------------------------------------------------------------------------- /macro.py: -------------------------------------------------------------------------------- 1 | # -*- encoding:utf8 -*- 2 | 3 | from selenium import webdriver 4 | from bs4 import BeautifulSoup 5 | import sys 6 | from PyQt4.QtCore import * 7 | from PyQt4.QtGui import * 8 | from PyQt4 import QtGui, QtCore 9 | import time 10 | from selenium.webdriver.common.by import By 11 | from selenium.webdriver.support.ui import WebDriverWait 12 | from selenium.webdriver.support import expected_conditions as EC 13 | 14 | 15 | userID = "your id" 16 | userPW = "your password" 17 | userNum = "990101" # 생년월일 18 | 19 | # url 20 | userSearch = "http://ticket.interpark.com/Ticket/Goods/GoodsInfo.asp?GroupCode=19015881" 21 | 22 | now = time.localtime() 23 | userTime = "1" 24 | userTicket = "일반" 25 | userBank = 38051 26 | userDate = "20191007" 27 | cbCheck = [1, 1, 1, 1, 1] 28 | 29 | chrome_driver='/usr/local/bin/chromedriver' 30 | driver = webdriver.Chrome(chrome_driver) 31 | wait = WebDriverWait(driver, 1) 32 | driver.implicitly_wait(1) 33 | g_dnow = "" 34 | 35 | def log_in(): 36 | try: 37 | login_url = "https://ticket.interpark.com/Gate/TPLogin.asp?CPage=B&MN=Y&tid1=main_gnb&tid2=right_top&tid3=login&tid4=login" 38 | driver.get(login_url) 39 | driver.switch_to_frame(driver.find_element_by_tag_name("iframe")); 40 | time.sleep(0.5) 41 | driver.find_element_by_id('userId').send_keys(userID) # ID 입력 42 | driver.find_element_by_id('userPwd').send_keys(userPW) # PW 입력 43 | driver.find_element_by_id('btn_login').click() 44 | wait.until(EC.presence_of_element_located((By.ID, "logstatus"))) 45 | except: 46 | print("got exception(log_in)") 47 | 48 | def move_to_ticket_page(): 49 | try: 50 | driver.get(userSearch) 51 | # 공연 기간 정보 가져오기 52 | global g_dnow 53 | g_dnow = driver.find_element_by_xpath('//p[@class="time"]').text 54 | g_dnow = g_dnow[g_dnow.find('~ ') + 2:].replace('.', '') 55 | except: 56 | print("got exception(move_to_ticket_page)") 57 | 58 | def open_reservation_page(): 59 | try: 60 | driver.execute_script('javascript:fnNormalBooking();') 61 | driver.switch_to.window(driver.window_handles[1]) 62 | except: 63 | print("got exception(open_reservation_page)") 64 | 65 | def select_date_and_time(): 66 | try: 67 | # 날짜 68 | # 1단계 프레임 받아오기 69 | frame = wait.until(EC.presence_of_element_located((By.ID, "ifrmBookStep"))) 70 | driver.switch_to.frame(frame) 71 | 72 | # 달(月) 바꾸기 73 | driver.execute_script("javascript: fnChangeMonth('" + userDate[:6] + "');") 74 | # 날짜 선택하기 75 | try: 76 | # 달력 정보가 존재할 경우 77 | # 달력 정보 가져오기 78 | wait.until(EC.presence_of_element_located((By.ID, "CellPlayDate"))) 79 | time.sleep(0.5) 80 | bs4 = BeautifulSoup(driver.page_source, "html.parser") 81 | calender = bs4.findAll('a', id='CellPlayDate') 82 | elem = calender[0]["onclick"] 83 | 84 | # 사용자의 입력값과 일치하는 함수를 찾는다. 85 | for i in range(0, len(calender)): 86 | if "fnSelectPlayDate(" + str(i) + ", '" + userDate + "')" == calender[i]["onclick"]: 87 | elem = calender[i]["onclick"] 88 | print("same with input date %s"%elem) 89 | break 90 | 91 | # 해당 날짜 선택하기 92 | print("selected date: %s"%elem) 93 | driver.execute_script("javascript: " + elem) 94 | except: 95 | print("no date info") 96 | print(g_dnow) 97 | # 달력 정보가 존재하지 않을 경우 98 | # 공연가능한 마지막 달로 이동한다 99 | driver.execute_script("javascript: fnChangeMonth('" + g_dnow[:6] + "');") 100 | # 달력 정보 가져오기 101 | wait.until(EC.presence_of_element_located((By.ID, "CellPlayDate"))) 102 | time.sleep(0.5) 103 | bs4 = BeautifulSoup(driver.page_source, "html.parser") 104 | calender = bs4.findAll('a', id='CellPlayDate') 105 | select_script = calender[len(calender) - 1]["onclick"] 106 | # 해당 날짜 선택하기 107 | driver.execute_script("javascript:" + select_script) 108 | 109 | # 페이지 로딩 대기 110 | wait.until(EC.presence_of_element_located((By.ID, "CellPlaySeq"))) 111 | 112 | # 회차 113 | # 회차 정보 가져오기 114 | bs4 = BeautifulSoup(driver.page_source, "html.parser") 115 | timeList = bs4.find('div', class_='scrollY').find('span', id='TagPlaySeq').findAll('a', id='CellPlaySeq') 116 | 117 | # 회차 유효성 검사 118 | try: 119 | if int(userTime[0]) <= len(timeList): elem = timeList[int(userTime[0]) - 1]["onclick"] 120 | else: elem = timeList[0]["onclick"] 121 | except: elem = timeList[0]["onclick"] 122 | 123 | # 회차 선택하기 124 | driver.execute_script("javascript:" + elem) 125 | print(elem) 126 | 127 | # 다음단계 128 | # 메인 프레임 받아오기 129 | driver.switch_to.default_content() 130 | 131 | # 2단계 넘어가기 132 | driver.execute_script("javascript:fnNextStep('P');") 133 | 134 | # 당일 예매 경고창 감지 135 | try: 136 | alert = driver.switch_to_alert() 137 | alert.accept() 138 | except: 139 | elem = '' 140 | 141 | except: 142 | # 관람일/회차선택 단계가 없을 경우 143 | elem='' 144 | print("process00 %s"%sys.exc_info()) 145 | 146 | def is_preferred_and_available_seat(seat): 147 | if seat is None: 148 | return None 149 | 150 | # seat = seatList[j] 151 | text = seat['alt'][seat['alt'].find('[') + 1:] 152 | if (text.find("VIP") != -1) & (cbCheck[0] == 1): 153 | return True 154 | if (text.find("R") != -1) & (cbCheck[1] == 1): 155 | return True 156 | if (text.find("S") != -1) & (cbCheck[2] == 1): 157 | return True 158 | if (text.find("A") != -1) & (cbCheck[3] == 1): 159 | return True 160 | if cbCheck[4] == 1: 161 | return True 162 | 163 | return False 164 | 165 | def selct_ticket_price(): 166 | try: 167 | # 가격/할인선택 (3단계) 168 | # 3단계 프레임 받아오기 169 | driver.switch_to.default_content() 170 | wait.until(EC.presence_of_element_located((By.ID, "ifrmBookStep"))) 171 | frame = driver.find_element_by_id('ifrmBookStep') 172 | driver.switch_to.frame(frame) 173 | 174 | # 표 선택하기 175 | # 표 정보를 가져온다. 176 | bs4 = BeautifulSoup(driver.page_source, "html.parser") 177 | ticketList = bs4.findAll('select') 178 | 179 | # 사용자의 입력값과 일치하는 함수를 찾는다. 180 | for i in range(0, len(ticketList)): 181 | ticketStr = ticketList[i]["pricegradename"] 182 | if ticketStr.find(userTicket) != -1: 183 | # 사용자의 입력값과 일치하는 선택지가 있다면 184 | elem = ticketList[i]["index"] 185 | break 186 | 187 | # 표 선택하기 188 | try: driver.find_element_by_xpath("//td[@class='taL']//select[@index='" + str(elem) + "']//option[@value='1']").click() 189 | except: driver.find_element_by_xpath("//td[@class='taL']//select[@pricegrade='01']//option[@value='1']").click() 190 | 191 | # 다음단계 192 | # 메인 프레임 받아오기 193 | driver.switch_to.default_content() 194 | time.sleep(0.5) 195 | # 4단계 넘어가기 196 | driver.execute_script("javascript:fnNextStep('P');") 197 | 198 | # 특수표 경고창 감지 199 | try: 200 | alert = driver.switch_to_alert() 201 | alert.accept() 202 | except: 203 | elem = '' 204 | except: 205 | print("got error(selct_ticket_price)") 206 | 207 | def fill_order(): 208 | try: 209 | # print(driver.page_source) 210 | wait.until(EC.presence_of_element_located((By.ID, "ifrmBookStep"))) 211 | frame = driver.find_element_by_id('ifrmBookStep') 212 | driver.switch_to.frame(frame) 213 | time.sleep(0.5) 214 | # print(driver.page_source) 215 | # send number 216 | wait.until(EC.presence_of_element_located((By.ID, "YYMMDD"))) 217 | # birth_day.send_keys(userNum) 218 | driver.find_element_by_xpath("//td[@class='form']//input[@id='YYMMDD']").send_keys(userNum) 219 | # move to next 220 | driver.switch_to.default_content() 221 | driver.execute_script("javascript:fnNextStep('P');") 222 | 223 | 224 | wait.until(EC.presence_of_element_located((By.ID, "ifrmBookStep"))) 225 | frame = driver.find_element_by_id('ifrmBookStep') 226 | driver.switch_to.frame(frame) 227 | 228 | elem = driver.find_element_by_xpath("//tr[@id='Payment_22004']//input[@name='Payment']") 229 | elem.click() 230 | # elem = wait.until(EC.presence_of_element_located((By.ID, "BankCode"))) 231 | elem = driver.find_element_by_xpath("//select[@id='BankCode']//option[@value='" + str(userBank) + "']") 232 | elem.click() 233 | 234 | driver.switch_to.default_content() 235 | driver.execute_script("javascript:fnNextStep('P');") 236 | except: 237 | print("got error(fill_order)") 238 | 239 | def pay_for_ticket(): 240 | try: 241 | wait.until(EC.presence_of_element_located((By.ID, "ifrmBookStep"))) 242 | frame = driver.find_element_by_id('ifrmBookStep') 243 | driver.switch_to.frame(frame) 244 | # 약관 동의 245 | # 취소수수료/취소기한을 확인하였으며, 동의합니다. 246 | elem = driver.find_element_by_xpath("//input[@id='CancelAgree']") 247 | elem.click() 248 | # 제3자 정보제공 내용에 동의합니다. 249 | elem = driver.find_element_by_xpath("//input[@id='CancelAgree2']") 250 | elem.click() 251 | except: 252 | print("got error(pay_for_ticket)") 253 | 254 | 255 | def select_seat(): 256 | select_seat_internal() 257 | 258 | def select_seat_internal(): 259 | succeeded = False 260 | # 2단계 프레임 받아오기 261 | try: 262 | driver.switch_to.default_content() 263 | frame = driver.find_element_by_id('ifrmSeat') 264 | driver.switch_to.frame(frame) 265 | 266 | # 미니맵 존재여부 검사 267 | try: 268 | wait.until(EC.presence_of_element_located((By.ID, "ifrmSeatView"))) 269 | frame = driver.find_element_by_id('ifrmSeatView') 270 | driver.switch_to.frame(frame) 271 | wait.until(EC.presence_of_element_located((By.NAME, "Map"))) 272 | bs4 = BeautifulSoup(driver.page_source, "html.parser") 273 | elem = bs4.find('map') 274 | except: 275 | elem = None 276 | 277 | time.sleep(0.5) 278 | # 좌석 프레임 받아오기 279 | driver.switch_to.default_content() 280 | wait.until(EC.presence_of_element_located((By.ID, "ifrmSeat"))) 281 | frame = driver.find_element_by_id('ifrmSeat') 282 | driver.switch_to.frame(frame) 283 | frame = driver.find_element_by_id('ifrmSeatDetail') 284 | driver.switch_to.frame(frame) 285 | # 좌석 정보를 읽어온다. 286 | bs4 = BeautifulSoup(driver.page_source, "html.parser") 287 | seatList = bs4.findAll('img', class_='stySeat') 288 | print("got seat list: %d"%(len(seatList))) 289 | 290 | # 좌석이 존재할 경우 error X -> except 실행 X 291 | for i in range(0, len(seatList)): 292 | seat = seatList[i] 293 | if is_preferred_and_available_seat(seat): 294 | # 좌석 선택하기 295 | try: 296 | driver.execute_script(seat['onclick'] + ";") 297 | # 2단계 프레임 받아오기 298 | driver.switch_to.default_content() 299 | wait.until(EC.presence_of_element_located((By.ID, "ifrmSeat"))) 300 | frame = driver.find_element_by_id('ifrmSeat') 301 | driver.switch_to.frame(frame) 302 | # 3단계 넘어가기 303 | driver.execute_script("javascript:fnSelect();") 304 | succeeded = True 305 | # 경고창 감지 306 | try: 307 | alert = driver.switch_to_alert() 308 | alert.accept() 309 | time.sleep(0.5) 310 | succeeded = False 311 | continue 312 | except: 313 | elem = '' 314 | print("no alert window or got exception") 315 | 316 | break; 317 | except: 318 | print("try to move to final stage. but failed: %s"%seat) 319 | except: 320 | print("got unexpected except(select_seat)") 321 | 322 | return succeeded 323 | 324 | def esc(event): 325 | if event.key() == QtCore.Qt.Key_Escape : 326 | # 활동로그 327 | sys.exit() 328 | event.accept() 329 | 330 | 331 | if __name__ == '__main__': 332 | global login_btn, move_to_ticket_btn, win 333 | 334 | app = QtGui.QApplication(sys.argv) 335 | win = QWidget() 336 | 337 | login_btn = QPushButton("Log In", win) 338 | login_btn.toggle() 339 | login_btn.clicked.connect(log_in) 340 | 341 | move_to_ticket_btn = QPushButton("Move To Ticket Page", win) 342 | move_to_ticket_btn.toggle() 343 | move_to_ticket_btn.clicked.connect(move_to_ticket_page) 344 | 345 | open_reservation_btn = QPushButton("Open Reservation Page", win) 346 | open_reservation_btn.toggle() 347 | open_reservation_btn.clicked.connect(open_reservation_page) 348 | 349 | select_date_and_time_btn = QPushButton("Select date and time", win) 350 | select_date_and_time_btn.toggle() 351 | select_date_and_time_btn.clicked.connect(select_date_and_time) 352 | 353 | select_seat_btn = QPushButton("Select a seat", win) 354 | select_seat_btn.toggle() 355 | select_seat_btn.clicked.connect(select_seat) 356 | 357 | vlayout = QtGui.QVBoxLayout() 358 | vlayout.addWidget(login_btn) 359 | vlayout.addWidget(move_to_ticket_btn) 360 | vlayout.addWidget(open_reservation_btn) 361 | vlayout.addWidget(select_date_and_time_btn) 362 | vlayout.addWidget(select_seat_btn) 363 | win.setLayout(vlayout) 364 | 365 | win.setGeometry(820, 350, 310, 300) 366 | win.setContentsMargins(3, 1, 3, 2) 367 | win.setWindowTitle("InterMacro") 368 | win.setFont(QFont("consolas")) 369 | win.show() 370 | app.exec_() --------------------------------------------------------------------------------