수정 중
This commit is contained in:
parent
753960ccd3
commit
50cc450fe8
51
main.py
51
main.py
|
|
@ -156,7 +156,7 @@ class MainApp(QWidget):
|
||||||
self.paused = False
|
self.paused = False
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def load_excel(self):
|
def load_excel_ini(self):
|
||||||
fileName, _ = QFileDialog.getOpenFileName(self, 'Open Excel File', '', 'Excel Files (*.xlsx *.xls)')
|
fileName, _ = QFileDialog.getOpenFileName(self, 'Open Excel File', '', 'Excel Files (*.xlsx *.xls)')
|
||||||
if fileName:
|
if fileName:
|
||||||
byte_size = self.byteSizeSpinBox.value()
|
byte_size = self.byteSizeSpinBox.value()
|
||||||
|
|
@ -179,9 +179,41 @@ class MainApp(QWidget):
|
||||||
self.logger.error(f"파일을 불러오는 중 오류가 발생했습니다: {e}")
|
self.logger.error(f"파일을 불러오는 중 오류가 발생했습니다: {e}")
|
||||||
QMessageBox.critical(self, "오류", "파일을 불러오는 중 오류가 발생했습니다. 파일 형식을 확인해주세요.")
|
QMessageBox.critical(self, "오류", "파일을 불러오는 중 오류가 발생했습니다. 파일 형식을 확인해주세요.")
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def load_excel(self):
|
||||||
|
fileName, _ = QFileDialog.getOpenFileName(self, 'Open Excel File', '', 'Excel Files (*.xlsx *.xls)')
|
||||||
|
if fileName:
|
||||||
|
byte_size = self.byteSizeSpinBox.value()
|
||||||
|
try:
|
||||||
|
df = pd.read_excel(fileName, sheet_name=0)
|
||||||
|
first_column_name = df.columns[0]
|
||||||
|
non_empty_data = df[first_column_name].dropna()
|
||||||
|
unique_data = non_empty_data.drop_duplicates()
|
||||||
|
filtered_data = [data for data in unique_data if len(str(data).encode('utf-8')) >= byte_size]
|
||||||
|
|
||||||
|
# 데이터를 QTextEdit 위젯에 표시
|
||||||
|
self.dataDisplay.clear()
|
||||||
|
self.dataDisplay.append("\n".join([str(data) for data in filtered_data]))
|
||||||
|
|
||||||
|
# 프로그레스바와 레이블 업데이트
|
||||||
|
self.progressBar.setMaximum(len(unique_data))
|
||||||
|
self.loadedCountLabel.setText(f"불러온 갯수: {len(filtered_data)}")
|
||||||
|
|
||||||
|
self.deleting_data = filtered_data
|
||||||
|
self.logger.debug(f'{fileName} 파일에서 중복 제거 후 {len(filtered_data)}개의 적합한 데이터를 성공적으로 불러왔습니다.')
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"파일을 불러오는 중 오류가 발생했습니다: {e}")
|
||||||
|
QMessageBox.critical(self, "오류", "파일을 불러오는 중 오류가 발생했습니다. 파일 형식을 확인해주세요.")
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def start_browser_action(self):
|
def start_browser_action(self):
|
||||||
selected_market = next((btn.text() for btn in self.marketButtons.values() if btn.isChecked()), None)
|
selected_market = next((btn.text() for btn in self.marketButtons.values() if btn.isChecked()), None)
|
||||||
|
if not selected_market: # 마켓이 선택되지 않았을 경우
|
||||||
|
QMessageBox.warning(self, "경고", "마켓을 선택해주세요.")
|
||||||
|
return # 경고창을 표시하고 함수를 더 이상 진행하지 않음
|
||||||
|
|
||||||
if selected_market:
|
if selected_market:
|
||||||
urls = {
|
urls = {
|
||||||
"쿠팡": "https://xauth.coupang.com/auth/realms/seller/protocol/openid-connect/auth?response_type=code&client_id=wing&redirect_uri=https%3A%2F%2Fwing.coupang.com%2Fsso%2Flogin?returnUrl%3D%252F&state=794abcff-4d9d-4460-86d8-e0efba1b97c2&login=true&scope=openid",
|
"쿠팡": "https://xauth.coupang.com/auth/realms/seller/protocol/openid-connect/auth?response_type=code&client_id=wing&redirect_uri=https%3A%2F%2Fwing.coupang.com%2Fsso%2Flogin?returnUrl%3D%252F&state=794abcff-4d9d-4460-86d8-e0efba1b97c2&login=true&scope=openid",
|
||||||
|
|
@ -191,15 +223,16 @@ class MainApp(QWidget):
|
||||||
"ESM": "https://signin.esmplus.com/login"
|
"ESM": "https://signin.esmplus.com/login"
|
||||||
}
|
}
|
||||||
url = urls.get(selected_market)
|
url = urls.get(selected_market)
|
||||||
if not selected_market:
|
if url:
|
||||||
QMessageBox.warning(self, "경고", "마켓을 선택해 주세요.")
|
if self.browser_thread and self.browser_thread.isRunning():
|
||||||
|
QMessageBox.warning(self, "경고", "이미 실행 중인 작업이 있습니다.")
|
||||||
else:
|
else:
|
||||||
if self.browser_thread and self.browser_thread.isRunning():
|
self.browser_thread = BrowserThread(url, selected_market, self.deleting_data, self.logger)
|
||||||
QMessageBox.warning(self, "경고", "이미 실행 중인 작업이 있습니다.")
|
self.browser_thread.progress_updated.connect(self.update_progress_bar)
|
||||||
else:
|
self.browser_thread.start()
|
||||||
self.browser_thread = BrowserThread(url, selected_market, self.deleting_data, self.logger)
|
else:
|
||||||
self.browser_thread.progress_updated.connect(self.update_progress_bar)
|
QMessageBox.critical(self, "오류", "선택한 마켓의 URL 정보가 없습니다.")
|
||||||
self.browser_thread.start()
|
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def delete_products(self):
|
def delete_products(self):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
너는 파이썬 프로그래밍 전문가지? 나는 아래의 동작을 하는 간단한 프로그램을 만들고 싶어하는 사업가야. 내게 친절하고 자세하게 설명해 주면서 프로그램을 만드는데 도와줄꺼지??
|
||||||
|
하나하나 차근차근 단계별로 만들어가보자.
|
||||||
|
|
||||||
|
pyqt5로 gui인터페이스를 가진 간단한 프로그램을 만들어줘.
|
||||||
|
디버깅을 위해 아래에 첨부한 로깅모듈을 넣고, 레벨은 디버그로 설정, 각 동작마다 디버그메세지를 출력해줘. 콘솔과 파일 모두 출력되어야 해.
|
||||||
|
그리고 예외처리를 위해 모든 동작들은 강건하게 처리되어야 하고, 로그메세지에는 exc_info=true 설정이 되어있어야 해.
|
||||||
|
playwright동작 부분은 별도의 클래스와 클래스 메서드로 만들어져서 모듈화를 시키고 유지보수성을 향상시켜야 해.
|
||||||
|
|
||||||
|
GUI구성
|
||||||
|
화면구성은 간단해.
|
||||||
|
1. 엑셀파일 불러오기 버튼으로 엑셀파일을 불러와.
|
||||||
|
2. 라디오 박스가 5개 있어. 각 라벨은 "쿠팡, 스스, 11번가, 롯데온, ESM"이야.
|
||||||
|
3. 브라우저 실행 버튼이 있고, 이걸 누르면 비동기 playwright가 실행되. 이때 접속하는 url은 라디오 박스에서 선택한 사이트의 판매자센터로 접속해.
|
||||||
|
4. 삭제실행 버튼이 있고, 이걸 누르면 삭제메서드를 불러와 실행되.
|
||||||
|
5. 중지버튼이 있고, 이 버튼은 삭제실행의 메서드를 중지시켜. 이렇게 되면 중지 버튼의 텍스트는 "계속"으로 바껴.
|
||||||
|
6. 진행상태 프로그레스바 가 있어.
|
||||||
|
7. 항상위 버튼 체크박스가 있어. 이걸 체크하면 현재 실행중인 프로그램이 항상위로 설정되.
|
||||||
|
8. 인증요청 버튼이 있고, 해당 버튼을 누르면 서버에서 인증키를 발급하고 해당 키를 프로그램 설정에 자동으로 입력되.
|
||||||
|
|
||||||
|
|
||||||
|
# 로그 및 서버설정
|
||||||
|
프로그램이 실행되면 mongoDB를 이용해 사용자 인증을 거치고, 인증된 PC는 로그를 기록해야 해. 인증 콜렉션은
|
||||||
|
SideProject 데이터베이스에 WRMC_DeleteProduct_Log 콜렉션에 접속한 PC의 IP와 PC이름, 접속한 시간이 기록되어야 해.
|
||||||
|
(데이터베이스와 콜렉션이 존재하지 않으면 생성해야 해)
|
||||||
|
그리고 프로그램이 종료되면 (사용자가 종료하든, 예외발생이나 강제종료로 종료되든) 프로그램 로그를 해당 PC에 기록해 줘.
|
||||||
|
|
||||||
|
|
||||||
|
동작구성
|
||||||
|
1. 엑셀파일 불러오기 버튼을 누르면 파일 다이알로그가 열리고, 엑셀파일을 선택해.
|
||||||
|
그리고 입력받은 엑셀파일의 첫 열에서 값들을 가져와 일정 바이트 이상의 길이인 셀값을 deleting_data 리스트 변수로 넣어줘.
|
||||||
|
|
||||||
|
2. 보통 해당값은 10개~200개 사이의 값이 될꺼야. 이 전체 갯수는 진행상태 프로그레스 바에 사용될꺼야.
|
||||||
|
|
||||||
|
3. 라디오박스에서 선택되는 마켓들마다 playwright에서 접속하는 주소가 달라져.
|
||||||
|
쿠팡 : https://xauth.coupang.com/auth/realms/seller/protocol/openid-connect/auth?response_type=code&client_id=wing&redirect_uri=https%3A%2F%2Fwing.coupang.com%2Fsso%2Flogin?returnUrl%3D%252F&state=794abcff-4d9d-4460-86d8-e0efba1b97c2&login=true&scope=openid
|
||||||
|
스스 : https://accounts.commerce.naver.com/login?url=https%3A%2F%2Fsell.smartstore.naver.com%2F%23%2Flogin-callback
|
||||||
|
11번가 : https://login.11st.co.kr/auth/front/selleroffice/login.tmall?returnURL=https%3A%2F%2Fsoffice.11st.co.kr%2F
|
||||||
|
롯데온 : https://store.lotteon.com/cm/main/login_SO.wsp
|
||||||
|
ESM : https://signin.esmplus.com/login
|
||||||
|
|
||||||
|
4. 브라우저 실행버튼을 누르면 playwright는 비동기로 실행되어서 라디오 버튼에서 선택된 사이트로 접속되. 이때 gui프로그램의 응답성을 위해 비동기로 실행되고 사용자와 상호작용하도록 해야해. 실행모드는 headless=False야.
|
||||||
|
5. 브라우저가 실행되어 해당 마켓에 접속되면 사용자는 아이디와 비밀번호를 입력하고 해당 마켓에 접속 후, 상품수정페이지로 접속할 꺼야.
|
||||||
|
6. 상품수정 페이지에 접속완료되면 사용자는 삭제실행 버튼을 누를꺼야.
|
||||||
|
7. 삭제 실행 메서드의 동작은 playwright에서 접속된 사이트의 상품수정 페이지에서 동작해. deleting_data 리스트의 값을 순차적으로 "검색어" xpath에 넣고, "검색" xpath 버튼을 눌러. 그리고 검색결과가 나오면 "체크박스" xpath를 클릭하고, "삭제" xpath 버튼을 누를꺼야. 그럼 "검색결과" xpath에는 아무 데이터가 없어.
|
||||||
|
8. 7의 동작은 리스트에 있는 모든 값을 순차적으로 실행하면되. 1건 삭제동작이 성공하면 진행상태 프로그레스 바를 업데이트 시켜줘.
|
||||||
|
9. 동작 중 사용자가 중지버튼을 누르면 7의 동작은 중지되어야 해. 그리고 중지버튼의 텍스트는 "계속"으로 바껴. 그리고 사용자가 다시 "계속"텍스트로 바뀐 중지버튼을 누르면 7의 동작이 멈춘 부분부터 다시 재개되.
|
||||||
|
10. deleting_data 리스트의 끝에 다다르면 프로그레스바는 100%가 되어있을 것이고, 사용자에게 작업완료 라는 텍스트 메세지를 띄워줘.
|
||||||
|
|
||||||
|
11. 웹요소는 아래와 같아.
|
||||||
|
[ESM]
|
||||||
|
1.검색어 입력 박스
|
||||||
|
- 웹 요소 : <textarea id="txtKeyword" cols="10" rows="10" title="상품명 / SKU명 / 브랜드명 / 제조사명 / 관리코드 / SKU번호 / SKU관리코드 / ESM상품식별코드명 / ESM상품식별코드번호">상품명 / SKU명 / 브랜드명 / 제조사명 / 관리코드 / SKU번호 / SKU관리코드 / ESM상품식별코드명 / ESM상품식별코드번호</textarea>
|
||||||
|
- XPATH : /html/body/div[1]/div[3]/div[3]/div/div[1]/div[1]/dl[1]/dd/div/textarea
|
||||||
|
|
||||||
|
2.검색버튼
|
||||||
|
- 웹 요소 : <a href="javascript:;">
|
||||||
|
<img src="https://pics.esmplus.com/front/btn/btn_search1.gif" alt="검색하기" id="imgItemsSearch">
|
||||||
|
</a>
|
||||||
|
- XPATH : /html/body/div[1]/div[3]/div[3]/div/div[3]/div[2]/a[1]
|
||||||
|
|
||||||
|
3. 체크박스
|
||||||
|
- 웹 요소 체크상태 : <div id="gridcolumn-1014" class="x-unselectable x-column-header-checkbox x-column-header-align-left x-box-item x-column-header x-unselectable-default x-column-header-first" style="border-width: 1px; width: 24px; height: auto; left: 0px; top: 0px; margin: 0px;"><div id="gridcolumn-1014-titleEl" class="x-column-header-inner" style="height: auto; padding-top: 5px;"><span id="gridcolumn-1014-textEl" class="x-column-header-text"> </span></div><div id="gridcolumn-1014-clearEl" class="x-clear" role="presentation"></div></div>
|
||||||
|
- 웹 요소 체크해제상태 : <div id="gridcolumn-1014" class="x-unselectable x-column-header-checkbox x-column-header-align-left x-box-item x-column-header x-unselectable-default x-column-header-first x-grid-hd-checker-on" style="border-width: 1px; width: 24px; height: auto; left: 0px; top: 0px; margin: 0px;"><div id="gridcolumn-1014-titleEl" class="x-column-header-inner" style="height: auto; padding-top: 5px;"><span id="gridcolumn-1014-textEl" class="x-column-header-text"> </span></div><div id="gridcolumn-1014-clearEl" class="x-clear" role="presentation"></div></div>
|
||||||
|
- XPATH : /html/body/div[1]/div[4]/div[1]/div[2]/div/div[1]/div/div/div[1]
|
||||||
|
|
||||||
|
4.상품삭제버튼
|
||||||
|
- 웹 요소 : <span class="css_btn1">
|
||||||
|
<a href="javascript:;" onclick="javascript:ItemMngPopup.Open('SellStateDelete');">상품삭제</a>
|
||||||
|
</span>
|
||||||
|
- XPATH : /html/body/div[1]/div[4]/div[1]/div[1]/div[1]/span[9]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[쿠팡]
|
||||||
|
1.검색어 입력 박스
|
||||||
|
- 웹 요소 : <input data-v-671ac22c="" type="text" placeholder="두 글자 이상 입력" data-sc-common-uicfg="size:small" class="sc-common-input inputHeight32 inputWidth224">
|
||||||
|
- XPATH : /html/body/div[1]/div[2]/div/section/section/div/div/div[1]/div[5]/dd/div/dl[1]/dd[1]/span/table/tr[1]/td[2]/input
|
||||||
|
|
||||||
|
2.검색버튼
|
||||||
|
- 웹 요소 : <button data-v-671ac22c="" data-wuic-props="name:btn size:m type:primary" class="wing-web-component button searchBtn" style="margin-left: 8px;">
|
||||||
|
검색
|
||||||
|
</button>
|
||||||
|
- XPATH : /html/body/div[1]/div[2]/div/section/section/div/div/div[1]/div[5]/dd/div/dl[2]/dd/button[2]
|
||||||
|
|
||||||
|
3. 체크박스
|
||||||
|
- 웹 요소 : <span data-v-774563c0="" class="sc-common-check"><input data-v-774563c0="" type="checkbox"><i data-v-774563c0=""></i></span>
|
||||||
|
- XPATH : /html/body/div[1]/div[2]/div/section/section/div/div/div[1]/div[6]/div[2]/div[3]/div[1]/table/thead/tr/th[2]/span
|
||||||
|
|
||||||
|
4.상품삭제버튼
|
||||||
|
- 웹 요소 : <button data-v-29314869="" data-v-1b622923="" type="button" data-wuic-props="name:btn" class="wing-web-component wuic-button"><!---->
|
||||||
|
삭제
|
||||||
|
<!----></button>
|
||||||
|
- XPATH : /html/body/div[1]/div[2]/div/section/section/div/div/div[1]/div[6]/div[2]/div[2]/div[3]/div[1]/button[3]
|
||||||
|
|
||||||
|
[스스]
|
||||||
|
1.검색어 입력 박스
|
||||||
|
- 웹 요소 : <input type="text" class="form-control ng-pristine ng-untouched ng-valid ng-empty ng-valid-pattern ng-valid-maxlength" id="prd_name" name="productName" ng-model="vm.searchFormData.productName" maxlength="100" maxlength-err-type="max.product.productName" ng-pattern="/^[^\\*?"<>]+$/" ng-pattern-err-type="invalidSpecialCharacter" ncp-message-container="#error_searchKeyword_productName" ncp-input-clear="">
|
||||||
|
- XPATH : /html/body/ui-view[1]/div[3]/div/div[3]/div/ui-view/div[2]/ui-view[1]/div[2]/form/div[1]/div/ul/li[1]/div/div/div[3]/div[1]/div[2]/div/input
|
||||||
|
|
||||||
|
2.검색버튼
|
||||||
|
- 웹 요소 : <button type="submit" class="btn btn-primary" ng-click="vm.viewData.selectedProductStatusType = undefined" data-nclicks-code="sgn.search">검색</button>
|
||||||
|
- XPATH : /html/body/ui-view[1]/div[3]/div/div[3]/div/ui-view/div[2]/ui-view[1]/div[2]/form/div[2]/div/button[1]
|
||||||
|
|
||||||
|
3. 체크박스
|
||||||
|
- 웹 요소 : <input type="checkbox" class="ag-selection-checkbox" data-nclicks-code="itg.allcheck">
|
||||||
|
- XPATH : /html/body/ui-view[1]/div[3]/div/div[3]/div/ui-view/div[2]/ui-view[2]/div[1]/div[2]/div[3]/div/div/div/div/div[1]/div[1]/div/div[1]/div[2]/div/label/input
|
||||||
|
|
||||||
|
4.상품삭제버튼
|
||||||
|
- 웹 요소 : <button type="submit" class="btn btn-default btn-sm" ng-click="vm.func.changeProductStatus('DELETE')" data-nclicks-code="itl.delete">선택삭제</button>
|
||||||
|
- XPATH : /html/body/ui-view[1]/div[3]/div/div[3]/div/ui-view/div[2]/ui-view[2]/div[1]/div[2]/div[1]/div[1]/div/div[1]/button
|
||||||
|
|
||||||
|
[롯데온]
|
||||||
|
1.검색어 입력 박스
|
||||||
|
- 웹 요소 : <input id="mf_tac_layout_contents_ML000000055_body_ibx_spdNm" style="width:150px;" class="w2input wq_ipt" type="text">
|
||||||
|
- XPATH : /html/body/div[1]/div/div[3]/div[2]/div/div[2]/div[2]/div/div/div[3]/div/div[1]/table/tbody[1]/tr[1]/td[2]/input
|
||||||
|
|
||||||
|
2.검색버튼
|
||||||
|
- 웹 요소 : <input type="button" tabindex="0" id="mf_tac_layout_contents_ML000000055_body_btn_trigger7" class="w2trigger btn_cm point search " value="조회">
|
||||||
|
- XPATH : /html/body/div[1]/div/div[3]/div[2]/div/div[2]/div[2]/div/div/div[3]/div/div[2]/input[2]
|
||||||
|
|
||||||
|
3. 체크박스
|
||||||
|
- 웹 요소 : <input type="checkbox" name="wq_uuid_1420_header__column9_checkboxLabel_" id="wq_uuid_1420_header__column9_checkboxLabel__id" colid="">
|
||||||
|
- XPATH : /html/body/div[1]/div/div[3]/div[2]/div/div[2]/div[2]/div/div/div[4]/div[2]/div[2]/div/div[1]/div/table/thead[2]/tr/th[2]/input
|
||||||
|
|
||||||
|
4.상품삭제버튼
|
||||||
|
- 웹 요소 : <input type="button" tabindex="0" id="mf_tac_layout_contents_ML000000055_body_btn_delSpd" class="w2trigger btn_cm sm point3" value="선택상품삭제">
|
||||||
|
- XPATH : /html/body/div[1]/div/div[3]/div[2]/div/div[2]/div[2]/div/div/div[4]/div[2]/div[1]/div[2]/input[8]
|
||||||
|
|
||||||
|
[11번가]
|
||||||
|
1.검색어 입력 박스
|
||||||
|
- 웹 요소 : <input id="prdNm" name="prdNm" type="text" class="text" value="" style="width: 160px;" onclick="setPrdNm(); doCommonStat('NPSB004');" onkeypress="goEnterCheck();" onkeydown="if(event.keyCode == 13){return false;}">
|
||||||
|
- XPATH : /html/body/div[3]/div[1]/form/div/div[2]/div[2]/div[1]/table/tbody[1]/tr[1]/td[1]/input[1]
|
||||||
|
|
||||||
|
2.검색버튼
|
||||||
|
- 웹 요소 : <button type="button" class="defbtn_lar ladtype defbtn_seh" id="btnSearch" onclick="prdNoCheck();"><span><span>검색</span></span></button>
|
||||||
|
- XPATH : /html/body/div[3]/div[1]/form/div/div[2]/div[2]/div[2]/div/button[1]
|
||||||
|
|
||||||
|
3. 체크박스
|
||||||
|
- 웹 요소 체크상태 : <div style="cursor: pointer; margin-left: 5px; top: 50%; margin-top: -8px; position: relative; width: 16px; height: 16px;" id="jqxWidget530c321e" role="checkbox" aria-checked="true" aria-disabled="false"><div class="jqx-checkbox-default jqx-fill-state-normal jqx-rc-all"><div style="width: 13px; height: 13px;"><span style="width: 13px; height: 13px;" class="jqx-checkbox-check-checked"></span></div></div><input type="hidden" value="true"></div>
|
||||||
|
- 웹 요소 체크해제상태 : <div style="cursor: pointer; margin-left: 5px; top: 50%; margin-top: -8px; position: relative; width: 16px; height: 16px;" id="jqxWidget530c321e" role="checkbox" aria-checked="false" aria-disabled="false"><div class="jqx-checkbox-default jqx-fill-state-normal jqx-rc-all"><div style="width: 13px; height: 13px;"><span style="width: 13px; height: 13px;" class=""></span></div></div><input type="hidden" value="false"></div>
|
||||||
|
- XPATH : /html/body/div[3]/div[2]/div/div[2]/div/div[3]/div[1]/div/div[1]/div/div
|
||||||
|
- XPATH : /html/body/div[3]/div[2]/div/div[2]/div/div[3]/div[1]/div/div[1]/div/div/input
|
||||||
|
|
||||||
|
4.상품삭제버튼
|
||||||
|
- 웹 요소 : <a href="javascript:fnListDelete('U');" onclick="doCommonStat('NPOF017');" class="defbtn_lsm dtype3"><span>선택상품 삭제</span></a>
|
||||||
|
- XPATH : /html/body/div[3]/div[1]/div[5]/div/a[14]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from playwright.async_api import async_playwright
|
from playwright.async_api import async_playwright
|
||||||
import logging, random, os
|
import logging, random, os, time
|
||||||
|
|
||||||
# 로거 인스턴스 가져오기
|
# 로거 인스턴스 가져오기
|
||||||
logger = logging.getLogger('default_logger')
|
logger = logging.getLogger('default_logger')
|
||||||
|
|
@ -59,10 +59,28 @@ class MarketAutomation:
|
||||||
elif market == "쿠팡":
|
elif market == "쿠팡":
|
||||||
await self.page.fill('input[data-v-671ac22c][type="text"]', product_name)
|
await self.page.fill('input[data-v-671ac22c][type="text"]', product_name)
|
||||||
await self.page.click('button.searchBtn')
|
await self.page.click('button.searchBtn')
|
||||||
await self.page.wait_for_selector('span.sc-common-check input[type="checkbox"]') # 검색 결과 로드 대기
|
print("검색버큰 클릭")
|
||||||
await self.page.click('span.sc-common-check input[type="checkbox"]')
|
# await self.page.wait_for_selector('#rootContainer > div:nth-child(6) > div.search-result-section > div.table-wrapper-container > div.table-wrapper.border-right.hide-see-auto-option > table > thead > tr > th:nth-child(2) > span > input[type=checkbox]') # 검색 결과 로드 대기
|
||||||
|
# await self.page.wait_for_selector('#rootContainer > div:nth-child(6) > div.search-result-section > div.table-wrapper-container > div.table-wrapper.border-right.hide-see-auto-option > table > thead > tr > th:nth-child(2) > span > input[type=checkbox]') # 검색 결과 로드 대기
|
||||||
|
time.sleep(2)
|
||||||
|
# 검색된상품의 텍스트가 검색상품 텍스트와 일치하는지 검사.
|
||||||
|
await self.page.wait_for_selector('#rootContainer > div:nth-child(6) > div.search-result-section > div.table-wrapper-container > div.table-wrapper.border-right.hide-see-auto-option > table > tbody > tr > td.fixed-col.left-align.small-horizontal-padding.middle-vertical-padding.border-right-double.editable-cell.editable-popup > div > span')
|
||||||
|
|
||||||
|
# await self.page.locator('span.sc-common-check input[type="checkbox"]:nth-child(12)').click()
|
||||||
|
await self.page.click('#rootContainer > div:nth-child(6) > div.search-result-section > div.table-wrapper-container > div.table-wrapper.border-right.hide-see-auto-option > table > thead > tr > th:nth-child(2) > span > input[type=checkbox]')
|
||||||
|
print("체크박스 클릭")
|
||||||
|
|
||||||
|
|
||||||
|
# time.sleep(100)
|
||||||
await self.page.wait_for_selector('button[data-v-29314869]', state='attached')
|
await self.page.wait_for_selector('button[data-v-29314869]', state='attached')
|
||||||
await self.page.click('button[data-v-29314869]') # 삭제 버튼
|
print("삭제버튼 로드 기다림")
|
||||||
|
# CSS selector 사용
|
||||||
|
# await self.page.locator('button.wing-web-component.wuic-button:has-text("삭제")').click()
|
||||||
|
await self.page.locator('button.wing-web-component.wuic-button:has-text("삭제"):nth-child(4)').click()
|
||||||
|
print("삭제버튼 클릭")
|
||||||
|
time.sleep(100)
|
||||||
|
# Xpath 사용 (권장하지 않음)
|
||||||
|
# await self.page.locator("xpath=/html/body/div[1]/div[2]/div/section/section/div/div/div[1]/div[6]/div[2]/div[2]/div[3]/div[1]/button[3]").click()
|
||||||
await self.page.wait_for_load_state('networkidle') # 다음 동작 전 페이지 로딩 완료 대기
|
await self.page.wait_for_load_state('networkidle') # 다음 동작 전 페이지 로딩 완료 대기
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue