본문 바로가기
프로그래밍/Python

인터파크 티켓팅 매크로 코드 공유

by 페이지다운 2020. 4. 11.
반응형

이번엔 인터파크 티켓팅 매크로에 대한 코드를 공유하겠다. 자유롭게 수정해서 쓰고, 재배포 시 출처를 명확히 표기할 경우 허용하지만, 상업적 용도로 내 코드 전체 또는 일부를 사용하는 것은 금지다.

 

일반 버전

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#인터파크 티켓팅 매크로
#만든 이 : PageDown
#https://pagedown.n-e.kr/
 
 
import sys
import random
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from selenium import webdriver
 
form_class = uic.loadUiType('TicketingMacro.ui')[0
 
 
class MyWindow(QtWidgets.QMainWindow, form_class):
    is_data_disabled = False  # 정보 입력창 비활성화 여부
    time = None  # 스레드로부터 받아오는 현재 시간
 
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.user_password_input.setEchoMode(QtWidgets.QLineEdit.Password)
        self.seats_number_spinbox.setValue(1)
        self.date_edit.setDate(QtCore.QDate.currentDate())
        self.time_edit.setTime(QtCore.QTime.currentTime())
        self.disableElements(self.time_edit, self.label_auto_start)
 
        self.apply_btn.clicked.connect(self.initData)  # 확인 버튼 클릭
        self.login_btn.clicked.connect(self.startLogin)  # 로그인 버튼 클릭
        self.start_ticketing_btn.clicked.connect(self.startTicketing)  # 시작 버튼 클릭
        self.seats_number_spinbox.valueChanged.connect(self.checkSeatsNumber)  # 좌석 개수 변경
        self.use_auto_start.stateChanged.connect(self.useAutoStart)  # 자동 시작 여부 변경
 
    def initData(self):
        if not self.is_data_disabled:  # 정보 입력창 활성화 시
            self.user_id = self.user_id_input.text()  # 아이디
            self.user_password = self.user_password_input.text()  # 비밀번호
            self.product_code = self.product_code_input.text()  # 공연 코드
            self.seats_number = self.seats_number_spinbox.value()  # 좌석 개수
            self.use_random_seat = self.use_seat_select.isChecked()  # 중간(랜덤) 선택 여부
            self.is_canceled_ticketing = self.canceled_ticket_mode.isChecked()  # 취켓팅 여부
            if self.use_auto_start.isChecked():  # 자동 시작 시 시간 24시간 방식 -> 12시간 방식 변경
                self.time = self.time_edit.time()
                self.time = str(self.time.toPyTime())[:6+ '00'
                if int(self.time[0:2]) > 12:
                    self.new_time_h = int(self.time[0:2]) - 12
                    self.time = str(self.new_time_h) + self.time[2:]
                    if self.new_time_h < 10:
                        self.time = '0' + self.time
                elif self.time[0:2== '00':
                    self.time = '12' + self.time[2:]
            self.date = self.date_edit.date()  # 날짜
            self.date = self.date.toPyDate()
            self.date = str(self.date).replace('-''')
            self.disableElements(self.user_id_input, self.user_password_input, self.product_code_input, self.date_edit,
                                 self.seats_number_spinbox, self.use_seat_select, self.use_auto_start, self.time_edit, self.canceled_ticket_mode)  # 요소 비활성화
            self.is_data_disabled = True
            self.apply_btn.setText('수정')
            QtWidgets.QMessageBox.information(self, '완료''정보 입력이 완료되었습니다.')
        else:
            self.is_data_disabled = False
            self.apply_btn.setText('확인')
            self.enableElements(self.user_id_input, self.user_password_input, self.product_code_input, self.date_edit,
                                self.seats_number_spinbox, self.use_seat_select, self.use_auto_start, self.time_edit, self.canceled_ticket_mode)  # 요소 활성화
 
    # 좌석 개수 변경
    def checkSeatsNumber(self):
        if self.seats_number_spinbox.value() > 4:
            self.seats_number_spinbox.setValue(4)
            QtWidgets.QMessageBox.critical(self, '오류''최대 좌석 개수는 4개입니다.')
        if self.seats_number_spinbox.value() < 1:
            self.seats_number_spinbox.setValue(1)
            QtWidgets.QMessageBox.critical(self, '오류''최소 좌석 개수는 1개입니다.')
 
    # 요소들 입력받아 한꺼번에 활성화
    def enableElements(self, *elements):
        for element in elements:
            element.setEnabled(True)
 
    # 요소들 입력받아 한꺼번에 비활성화
    def disableElements(self, *elements):
        for element in elements:
            element.setEnabled(False)
 
    # 자동시작 시
    def useAutoStart(self):
        if self.use_auto_start.isChecked():
            self.enableElements(self.time_edit, self.label_auto_start)
        else:
            self.time = None
            self.disableElements(self.time_edit, self.label_auto_start)
 
    # 로그인
    def startLogin(self):
        self.time_th = TimeThread()
        if self.apply_btn.text() == '확인':  # 확인 버튼을 누르지 않았을 때
            QtWidgets.QMessageBox.critical(self, '오류''좌석 정보를 입력하세요.')
        else:
            # Dictionary 형식으로 정보 전달
            ticketing_data_to_send = {
                'user_id': self.user_id,  # 아이디
                'user_pw': self.user_password,  # 비밀번호
                'product_code': self.product_code,  # 공연 번호
                'date': self.date,  # 공연 날짜
                'time': self.time,  # 자동시작 시간
                'seats_number': self.seats_number,  # 좌석 개수
                'use_random_seat': self.use_random_seat,  # 랜덤 선택 여부
                'time_signal': self.time_th.time_signal,  # 시간 스레드
                'is_canceled': self.is_canceled_ticketing  # 취켓팅 여부
            }
            self.apply_btn.setEnabled(False)  # 확인(수정) 버튼 비활성화
            self.login_btn.setEnabled(False)  # 로그인 버튼 비활성화
            # 시간/티켓팅 스레드 시작
            self.time_th.start()
            self.time_th.time_signal.connect(self.changeTime)
            self.ticketing_th = TicketingThread(ticketing_data=ticketing_data_to_send)
            self.ticketing_th.start()
 
    # 티켓팅 시작
    def startTicketing(self):
        self.ticketing_th.start_ticketing = True
 
    # 타이머 시작
    def startTimer(self):
        self.time_th = TimeThread()
        self.time_th.start()
        self.time_th.time_signal.connect(self.changeTime)
 
    @QtCore.pyqtSlot(str)
    def changeTime(self, time):
        if time == 'error':  # 웹드라이버(시계) 종료시 알림
            QtWidgets.QMessageBox.critical(self, '오류''타이머를 끄지 마십시오.')
        else:
            self.now_time.setText(time)
 
 
class TimeThread(QtCore.QThread):
    time_signal = QtCore.pyqtSignal(str)
 
    def run(self):
        while True:
            self.driver = webdriver.Chrome('chromedriver.exe')
            #네이버 시계 접속
            self.driver.get('https://search.naver.com/search.naver?sm=tab_hty.top&where=nexearch&query=%EB%84%A4%EC%9D%B4%EB%B2%84+%EC%8B%9C%EA%B3%84&oquery=%EC%8B%9C%EA%B3%84&tqi=UEMiLdprvTossFQzFhCssssssKG-165498')
            while True:
                try:
                    text = self.driver.find_element_by_css_selector('#_cs_domestic_clock > div._timeLayer.time_bx > div > div').text  # 네이버 시계 text
                    text = text.replace('\n''').replace(' ''')[0:8]
                except:
                    self.time_signal.emit('error')  # 웹드라이버(시계) 종료 시 error emit
                    break
                self.time_signal.emit(''.join(text))
 
 
class TicketingThread(QtCore.QThread):
    def __init__(self, ticketing_data, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.user_id = ticketing_data['user_id']
        self.user_password = ticketing_data['user_pw']
        self.product_code = ticketing_data['product_code']
        self.date = ticketing_data['date']
        self.set_time = ticketing_data['time']
        self.seats_number = ticketing_data['seats_number']
        self.use_random_seat = ticketing_data['use_random_seat']
        self.time_signal = ticketing_data['time_signal']
        self.is_canceled_ticketing = ticketing_data['is_canceled']
        self.driver = self.driver = webdriver.Chrome('chromedriver.exe')
        self.time_signal.connect(self.checkTime)  # 시간 스레드와 연결(자동시작)
        self.is_logined = False  # 로그인 여부 False
        self.start_ticketing = False  # 시작 여부 False
 
    # 요소는 반드시 CSS 셀렉터로만(ID 우선)
 
    # 로그인
    def login(self):
        self.driver.get('https://ticket.interpark.com/Gate/TPLogin.asp')
        iframes = self.driver.find_elements_by_tag_name('iframe')
        self.driver.switch_to.frame(iframes[0])
        self.driver.find_element_by_id('userId').send_keys(self.user_id)
        self.driver.find_element_by_id('userPwd').send_keys(self.user_password)
        self.driver.find_element_by_id('btn_login').click()
        self.driver.get('https://ticket.interpark.com/')  # 인터파크 메인 페이지로 강제 접속
 
    def selectSeat(self):
        self.failed_to_get_ticket = False  # 취켓팅용-기본적으로 성공으로 표시
        # 직링 생성
        self.url = 'http://poticket.interpark.com/Book/BookSession.asp?GroupCode={}&Tiki=N&Point=N&PlayDate={}&PlaySeq=001&BizCode=&BizMemberCode='.format(self.product_code, self.date)
        self.driver.get(self.url)
 
        # 요소를 찾을 때까지 무한 루프(찾지 못하면 예외)
        while True:
            try:
                first_iframe = self.driver.find_element_by_id('ifrmSeat')  # 첫번째 아이프레임
            except:
                continue
            else:
                break
        self.driver.switch_to.frame(first_iframe)
        while True:
            try:
                next_iframe = self.driver.find_element_by_id('ifrmSeatDetail')  # 두번째 좌석선택 아이프레임
            except:
                continue
            else:
                break
        self.driver.switch_to.frame(next_iframe)
 
        self.loop_time = 0  #취켓팅용
        while True:
            try:
                elements = self.driver.find_elements_by_class_name('stySeat')
                self.loop_time += 1
                # 취켓팅하면서 루프를 100번 돌면 루프 탈출
                if self.is_canceled_ticketing and (self.loop_time > 100):
                    self.failed_to_get_ticket = True  # 잔여 좌석 인식 실패(개수 0)
                    break
            except:
                continue
            else:
                if len(elements) == 0:  # 예외는 나지 않았지만 좌석이 인식되지 못한 경우
                    continue
                else:
                    break
 
        while len(elements) > 0:
            # 첫번째부터 1개 선택
            if (not self.use_random_seat) and (self.seats_number == 1):
                self.driver.find_element_by_css_selector('#TmgsTable > tbody > tr > td > img:nth-child(3)').click()  # 바로 첫번째 요소 선택
 
            # 여러개 선택
            elif not self.use_random_seat:
                seat_count = 0
                for element in elements:
                    if seat_count < self.seats_number:
                        element.click()
                        seat_count += 1
 
            # 랜덤/여러개 선택
            elif self.use_random_seat:
                start_seat = random.randint(
                    int(len(elements) / 2), len(elements) - self.seats_number)
                for i in range(start_seat, start_seat + self.seats_number):
                    if start_seat < self.seats_number + start_seat:
                        elements[i].click()
            try:
                self.driver.switch_to.default_content()  # 제일 바깥으로 아이프레임 벗어남
                self.driver.switch_to.frame(first_iframe)  # 첫번째 아이프레임으로 다시 변경
                self.driver.find_element_by_id('NextStepImage').click()  # 다음 버튼 클릭
                self.driver.find_element_by_class_name('title')  # 임의의 요소 찾아서 크롬 종료 방어
            except:
                continue
            else:
                break
 
    @QtCore.pyqtSlot(str)
    def checkTime(self, data):
        self.time = data
 
    def run(self):
        # 로그인되지 않았다면 로그인
        if not self.is_logined:
            self.login()
            self.is_logined = True
        while True:
            if str(self.set_time) == str(self.time):  # 입력한 시간하고 일치한다면
                self.start_ticketing = True
            if self.start_ticketing:
                self.selectSeat()  # 티켓팅 시작
                if not self.is_canceled_ticketing:  # 취켓팅이 아니고 끝난 경우는 성공으로 처리
                    self.start_ticketing = False
                if not self.failed_to_get_ticket:  # 취켓팅인데 성공한 경우
                    self.start_ticketing = False
                if self.is_canceled_ticketing and self.failed_to_get_ticket:  # 취켓팅인데 실패한 경우
                    pass
 
 
if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()
 
cs

일반 버전 ui 파일

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>TicketingMacro</class>
 <widget class="QMainWindow" name="TicketingMacro">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>304</width>
    <height>429</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>인터파크 티켓팅 매크로</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QLabel" name="label_title">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
      <property name="text">
       <string>인터파크 티켓팅 매크로</string>
      </property>
      <property name="alignment">
       <set>Qt::AlignCenter</set>
      </property>
     </widget>
    </item>
    <item>
     <widget class="QGroupBox" name="basic_data_box">
      <property name="title">
       <string>기본 정보</string>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout_4">
       <item>
        <layout class="QHBoxLayout" name="horizontalLayout_0">
         <property name="topMargin">
          <number>0</number>
         </property>
         <item>
          <widget class="QLabel" name="label_id">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>아이디:</string>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QLineEdit" name="user_id_input"/>
         </item>
         <item>
          <widget class="QLabel" name="label_pw">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>비밀번호:</string>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QLineEdit" name="user_password_input"/>
         </item>
        </layout>
       </item>
       <item>
        <layout class="QHBoxLayout" name="horizentalLayout_1">
         <property name="sizeConstraint">
          <enum>QLayout::SetDefaultConstraint</enum>
         </property>
         <item>
          <widget class="QLabel" name="label_pd_code">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>공연코드: </string>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QLineEdit" name="product_code_input"/>
         </item>
         <item>
          <widget class="QLabel" name="label_date">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>날짜: </string>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QDateEdit" name="date_edit"/>
         </item>
        </layout>
       </item>
       <item>
        <layout class="QHBoxLayout" name="horizontalLayout_2">
         <property name="topMargin">
          <number>0</number>
         </property>
         <item>
          <widget class="QLabel" name="label_seat_num">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>좌석매수(최대 4개): </string>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QSpinBox" name="seats_number_spinbox"/>
         </item>
        </layout>
       </item>
       <item>
        <widget class="QCheckBox" name="use_seat_select">
         <property name="text">
          <string>중간부터 선택(랜덤)</string>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
    </item>
    <item>
     <widget class="QGroupBox" name="auto_start_box">
      <property name="title">
       <string>시간 설정</string>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout_3">
       <item>
        <widget class="QLabel" name="label_manual">
         <property name="text">
          <string>*로그인을 클릭하면 자동으로 시계가 시작합니다.
*오후 말고 항상 오전만을 선택하세요.</string>
         </property>
        </widget>
       </item>
       <item>
        <widget class="QCheckBox" name="use_auto_start">
         <property name="text">
          <string>자동 시작 기능 사용</string>
         </property>
        </widget>
       </item>
       <item>
        <layout class="QHBoxLayout" name="horizontalLayout_3">
         <property name="topMargin">
          <number>0</number>
         </property>
         <item>
          <widget class="QTimeEdit" name="time_edit"/>
         </item>
         <item>
          <widget class="QLabel" name="label_auto_start">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>에 티켓팅을 자동 시작합니다.</string>
           </property>
          </widget>
         </item>
        </layout>
       </item>
       <item>
        <widget class="QLabel" name="now_time">
         <property name="font">
          <font>
           <pointsize>20</pointsize>
          </font>
         </property>
         <property name="text">
          <string>00 : 00 : 00</string>
         </property>
         <property name="alignment">
          <set>Qt::AlignCenter</set>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
    </item>
    <item>
     <widget class="QGroupBox" name="canceled_ticket_box">
      <property name="title">
       <string>취켓팅</string>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout_5">
       <item>
        <widget class="QCheckBox" name="canceled_ticket_mode">
         <property name="text">
          <string>취켓팅 모드</string>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
    </item>
    <item>
     <widget class="QPushButton" name="apply_btn">
      <property name="text">
       <string>확인</string>
      </property>
     </widget>
    </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout_4">
      <item>
       <widget class="QPushButton" name="login_btn">
        <property name="text">
         <string>로그인</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="start_ticketing_btn">
        <property name="text">
         <string>시작</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <action name="action">
   <property name="text">
    <string>고급 설정</string>
   </property>
  </action>
  <action name="action_2">
   <property name="text">
    <string>도움말</string>
   </property>
  </action>
 </widget>
 <resources/>
 <connections/>
</ui>
 
cs

라이트 버전

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#인터파크 티켓팅 매크로 Light
#만든 이 : PageDown
#https://pagedown.n-e.kr/
 
 
import sys
import random
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from selenium import webdriver
 
form_class = uic.loadUiType('TicketingMacro_Light.ui')[0
 
 
class MyWindow(QtWidgets.QMainWindow, form_class):
    is_data_disabled = False  # 정보 입력창 비활성화 여부
    time = None  # 스레드로부터 받아오는 현재 시간
 
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.user_password_input.setEchoMode(QtWidgets.QLineEdit.Password)
        self.seats_number_spinbox.setValue(1)
        self.date_edit.setDate(QtCore.QDate.currentDate())
        self.time_edit.setTime(QtCore.QTime.currentTime())
        self.disableElements(self.time_edit, self.label_auto_start)
 
        self.apply_btn.clicked.connect(self.initData)  # 확인 버튼 클릭
        self.login_btn.clicked.connect(self.startLogin)  # 로그인 버튼 클릭
        self.start_ticketing_btn.clicked.connect(self.startTicketing)  # 시작 버튼 클릭
 
        self.seats_number_spinbox.valueChanged.connect(self.checkSeatsNumber)  # 좌석 개수 변경
        self.use_auto_start.stateChanged.connect(self.useAutoStart)  # 자동 시작 여부 변경
 
    def initData(self):
        if not self.is_data_disabled:  # 정보 입력창 활성화 시
            self.user_id = self.user_id_input.text()  # 아이디
            self.user_password = self.user_password_input.text()  # 비밀번호
            self.product_code = self.product_code_input.text()  # 공연 코드
            self.seats_number = self.seats_number_spinbox.value()  # 좌석 개수
            self.use_random_seat = self.use_seat_select.isChecked()  # 중간(랜덤) 선택 여부
            if self.use_auto_start.isChecked():  # 자동 시작 시 시간 24시간 방식 -> 12시간 방식 변경
                self.time = self.time_edit.time()
                self.time = str(self.time.toPyTime())[:6+ '00'
                if int(self.time[0:2]) > 12:
                    self.new_time_h = int(self.time[0:2]) - 12
                    self.time = str(self.new_time_h) + self.time[2:]
                    if self.new_time_h < 10:
                        self.time = '0' + self.time
                elif self.time[0:2== '00':
                    self.time = '12' + self.time[2:]
            self.date = self.date_edit.date()  # 날짜
            self.date = self.date.toPyDate()
            self.date = str(self.date).replace('-''')
            self.disableElements(self.user_id_input, self.user_password_input, self.product_code_input, self.date_edit,
                                 self.seats_number_spinbox, self.use_seat_select, self.use_auto_start, self.time_edit)  # 요소 비활성화
            self.is_data_disabled = True
            self.apply_btn.setText('수정')
            QtWidgets.QMessageBox.information(self, '완료''정보 입력이 완료되었습니다.')
        else:
            self.is_data_disabled = False
            self.apply_btn.setText('확인')
            self.enableElements(self.user_id_input, self.user_password_input, self.product_code_input, self.date_edit,
                                self.seats_number_spinbox, self.use_seat_select, self.use_auto_start, self.time_edit)  # 요소 활성화
 
    # 좌석 개수 변경
    def checkSeatsNumber(self):
        if self.seats_number_spinbox.value() > 4:
            self.seats_number_spinbox.setValue(4)
            QtWidgets.QMessageBox.critical(self, '오류''최대 좌석 개수는 4개입니다.')
        if self.seats_number_spinbox.value() < 1:
            self.seats_number_spinbox.setValue(1)
            QtWidgets.QMessageBox.critical(self, '오류''최소 좌석 개수는 1개입니다.')
 
    # 요소들 입력받아 한꺼번에 활성화
    def enableElements(self, *elements):
        for element in elements:
            element.setEnabled(True)
 
    # 요소들 입력받아 한꺼번에 비활성화
    def disableElements(self, *elements):
        for element in elements:
            element.setEnabled(False)
 
    # 자동시작 시
    def useAutoStart(self):
        if self.use_auto_start.isChecked():
            self.enableElements(self.time_edit, self.label_auto_start)
        else:
            self.time = None
            self.disableElements(self.time_edit, self.label_auto_start)
 
    # 로그인
    def startLogin(self):
        self.time_th = TimeThread()
        if self.apply_btn.text() == '확인':  # 확인 버튼을 누르지 않았을 때
            QtWidgets.QMessageBox.critical(self, '오류''좌석 정보를 입력하세요.')
        else:
            # Dictionary 형식으로 정보 전달
            ticketing_data_to_send = {
                'user_id': self.user_id,  # 아이디
                'user_pw': self.user_password,  # 비밀번호
                'product_code': self.product_code,  # 공연 번호
                'date': self.date,  # 공연 날짜
                'time': self.time,  # 자동시작 시간
                'seats_number': self.seats_number,  # 좌석 개수
                'use_random_seat': self.use_random_seat,  # 랜덤 선택 여부
                'time_signal': self.time_th.time_signal  # 시간 스레드
            }
            self.apply_btn.setEnabled(False)  # 확인(수정) 버튼 비활성화
            self.login_btn.setEnabled(False)  # 로그인 버튼 비활성화
            # 시간/티켓팅 스레드 시작
            self.time_th.start()
            self.time_th.time_signal.connect(self.changeTime)
            self.ticketing_th = TicketingThread(ticketing_data=ticketing_data_to_send)
            self.ticketing_th.start()
 
    # 티켓팅 시작
    def startTicketing(self):
        self.ticketing_th.start_ticketing = True
 
    # 타이머 시작
    def startTimer(self):
        self.time_th = TimeThread()
        self.time_th.start()
        self.time_th.time_signal.connect(self.changeTime)
 
    @QtCore.pyqtSlot(str)
    def changeTime(self, time):
        if time == 'error':  # 웹드라이버(시계) 종료시 알림
            QtWidgets.QMessageBox.critical(self, '오류''타이머를 끄지 마십시오.')
        else:
            self.now_time.setText(time)
 
 
class TimeThread(QtCore.QThread):
    time_signal = QtCore.pyqtSignal(str)
 
    def run(self):
        while True:
            self.driver = webdriver.Chrome('chromedriver.exe')
            self.driver.get('https://search.naver.com/search.naver?sm=tab_hty.top&where=nexearch&query=%EB%84%A4%EC%9D%B4%EB%B2%84+%EC%8B%9C%EA%B3%84&oquery=%EC%8B%9C%EA%B3%84&tqi=UEMiLdprvTossFQzFhCssssssKG-165498')
            while True:
                try:
                    text = self.driver.find_element_by_css_selector('#_cs_domestic_clock > div._timeLayer.time_bx > div > div').text  # 네이버 시계 text
                    text = text.replace('\n''').replace(' ''')[0:8]
                except:
                    self.time_signal.emit('error')  # 웹드라이버(시계) 종료 시 error emit
                    break
                self.time_signal.emit(''.join(text))
 
 
class TicketingThread(QtCore.QThread):
    def __init__(self, ticketing_data, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.user_id = ticketing_data['user_id']
        self.user_password = ticketing_data['user_pw']
        self.product_code = ticketing_data['product_code']
        self.date = ticketing_data['date']
        self.set_time = ticketing_data['time']
        self.seats_number = ticketing_data['seats_number']
        self.use_random_seat = ticketing_data['use_random_seat']
        self.time_signal = ticketing_data['time_signal']
        self.driver = self.driver = webdriver.Chrome('chromedriver.exe')
        self.time_signal.connect(self.checkTime)  # 시간 스레드와 연결(자동시작)
        self.is_logined = False  # 로그인 여부 False
        self.start_ticketing = False  # 시작 여부 False
 
    # 요소는 반드시 CSS 셀렉터로만(ID 우선)
 
    # 로그인
    def login(self):
        self.driver.get('https://ticket.interpark.com/Gate/TPLogin.asp')
        iframes = self.driver.find_elements_by_tag_name('iframe')
        self.driver.switch_to.frame(iframes[0])
        self.driver.find_element_by_id('userId').send_keys(self.user_id)
        self.driver.find_element_by_id('userPwd').send_keys(self.user_password)
        self.driver.find_element_by_id('btn_login').click()
        self.driver.get('https://ticket.interpark.com/')  # 인터파크 메인 페이지로 강제 접속
 
    def selectSeat(self):
        # 직링 생성
        self.url = 'http://poticket.interpark.com/Book/BookSession.asp?GroupCode={}&Tiki=N&Point=N&PlayDate={}&PlaySeq=001&BizCode=&BizMemberCode='.format(
            self.product_code, self.date)
        self.driver.get(self.url)
 
        # 요소를 찾을 때까지 무한 루프(찾지 못하면 예외)
        while True:
            try:
                first_iframe = self.driver.find_element_by_id('ifrmSeat')  # 첫번째 아이프레임
            except:
                continue
            else:
                break
        self.driver.switch_to.frame(first_iframe)
        while True:
            try:
                next_iframe = self.driver.find_element_by_id('ifrmSeatDetail')  # 두번째 좌석선택 아이프레임
            except:
                continue
            else:
                break
        self.driver.switch_to.frame(next_iframe)
 
        while True:
            try:
                elements = self.driver.find_elements_by_class_name('stySeat')
            except:
                continue
            else:
                if len(elements) == 0:  # 예외는 나지 않았지만 좌석이 인식되지 못한 경우
                    continue
                else:
                    break
 
        while len(elements) > 0:
            # 첫번째부터 1개 선택
            if (not self.use_random_seat) and (self.seats_number == 1):
                self.driver.find_element_by_css_selector('#TmgsTable > tbody > tr > td > img:nth-child(3)').click()  # 바로 첫번째 요소 선택
 
            # 여러개 선택
            elif not self.use_random_seat:
                seat_count = 0
                for element in elements:
                    if seat_count < self.seats_number:
                        element.click()
                        seat_count += 1
 
            # 랜덤/여러개 선택
            elif self.use_random_seat:
                start_seat = random.randint(
                    int(len(elements) / 2), len(elements) - self.seats_number)
                for i in range(start_seat, start_seat + self.seats_number):
                    if start_seat < self.seats_number + start_seat:
                        elements[i].click()
            try:
                self.driver.switch_to.default_content()  # 제일 바깥으로 아이프레임 벗어남
                self.driver.switch_to.frame(first_iframe)  # 첫번째 아이프레임으로 다시 변경
                self.driver.find_element_by_id('NextStepImage').click()  # 다음 버튼 클릭
                self.driver.find_element_by_class_name('title')  # 임의의 요소 찾아서 크롬 종료 방어
            except:
                continue
            else:
                break
 
    @QtCore.pyqtSlot(str)
    def checkTime(self, data):
        self.time = data
 
    def run(self):
        # 로그인되지 않았다면 로그인
        if not self.is_logined:
            self.login()
            self.is_logined = True
        while True:
            if str(self.set_time) == str(self.time):  # 입력한 시간하고 일치한다면
                self.start_ticketing = True
            if self.start_ticketing:
                self.selectSeat()  # 티켓팅 시작
                self.start_ticketing = False  # 성공으로 처리
 
 
if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()
 
cs

라이트 버전 ui 파일

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>TicketingMacro_Light</class>
 <widget class="QMainWindow" name="TicketingMacro_Light">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>304</width>
    <height>375</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>인터파크 티켓팅 매크로 Light</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QLabel" name="label_title">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
      <property name="text">
       <string>인터파크 티켓팅 매크로</string>
      </property>
      <property name="alignment">
       <set>Qt::AlignCenter</set>
      </property>
     </widget>
    </item>
    <item>
     <widget class="QGroupBox" name="basic_data_box">
      <property name="title">
       <string>기본 정보</string>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout_4">
       <item>
        <layout class="QHBoxLayout" name="horizontalLayout_0">
         <property name="topMargin">
          <number>0</number>
         </property>
         <item>
          <widget class="QLabel" name="label_id">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>아이디:</string>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QLineEdit" name="user_id_input"/>
         </item>
         <item>
          <widget class="QLabel" name="label_pw">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>비밀번호:</string>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QLineEdit" name="user_password_input"/>
         </item>
        </layout>
       </item>
       <item>
        <layout class="QHBoxLayout" name="horizentalLayout_1">
         <property name="sizeConstraint">
          <enum>QLayout::SetDefaultConstraint</enum>
         </property>
         <item>
          <widget class="QLabel" name="label_pd_code">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>공연코드: </string>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QLineEdit" name="product_code_input"/>
         </item>
         <item>
          <widget class="QLabel" name="label_date">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>날짜: </string>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QDateEdit" name="date_edit"/>
         </item>
        </layout>
       </item>
       <item>
        <layout class="QHBoxLayout" name="horizontalLayout_2">
         <property name="topMargin">
          <number>0</number>
         </property>
         <item>
          <widget class="QLabel" name="label_seat_num">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>좌석매수(최대 4개): </string>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QSpinBox" name="seats_number_spinbox"/>
         </item>
        </layout>
       </item>
       <item>
        <widget class="QCheckBox" name="use_seat_select">
         <property name="text">
          <string>중간부터 선택(랜덤)</string>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
    </item>
    <item>
     <widget class="QGroupBox" name="auto_start_box">
      <property name="title">
       <string>시간 설정</string>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout_3">
       <item>
        <widget class="QLabel" name="label_manual">
         <property name="text">
          <string>*로그인을 클릭하면 자동으로 시계가 시작합니다.
*오후 말고 항상 오전만을 선택하세요.</string>
         </property>
        </widget>
       </item>
       <item>
        <widget class="QCheckBox" name="use_auto_start">
         <property name="text">
          <string>자동 시작 기능 사용</string>
         </property>
        </widget>
       </item>
       <item>
        <layout class="QHBoxLayout" name="horizontalLayout_3">
         <property name="topMargin">
          <number>0</number>
         </property>
         <item>
          <widget class="QTimeEdit" name="time_edit"/>
         </item>
         <item>
          <widget class="QLabel" name="label_auto_start">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="text">
            <string>에 티켓팅을 자동 시작합니다.</string>
           </property>
          </widget>
         </item>
        </layout>
       </item>
       <item>
        <widget class="QLabel" name="now_time">
         <property name="font">
          <font>
           <pointsize>20</pointsize>
          </font>
         </property>
         <property name="text">
          <string>00 : 00 : 00</string>
         </property>
         <property name="alignment">
          <set>Qt::AlignCenter</set>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
    </item>
    <item>
     <widget class="QPushButton" name="apply_btn">
      <property name="text">
       <string>확인</string>
      </property>
     </widget>
    </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout_4">
      <item>
       <widget class="QPushButton" name="login_btn">
        <property name="text">
         <string>로그인</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="start_ticketing_btn">
        <property name="text">
         <string>시작</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <action name="action">
   <property name="text">
    <string>고급 설정</string>
   </property>
  </action>
  <action name="action_2">
   <property name="text">
    <string>도움말</string>
   </property>
  </action>
 </widget>
 <resources/>
 <connections/>
</ui>
cs
반응형

댓글