import sys import re import shutil import os import json import logging from logging.handlers import RotatingFileHandler from openpyxl import load_workbook import pandas as pd import sqlite3 import requests import time from io import BytesIO from PIL import Image import xlwings as xw import pyautogui from datetime import datetime from baseDB import create_db, parse_data, read_excel_category_data, insert_category_data_to_db from naverParser import parse_naver_shopping from scraping_thread import ScrapingThread # 스크래핑 스레드 클래스 import from image_save_thread import ImageSaveThread # 이미지 스크래핑 스레드 클래스 import from xlwings_dialog import LogDialog from modules.automatch_tao import automatch from PyQt5 import QtCore, QtGui, QtWidgets, uic from PyQt5.QtCore import Qt, QUrl, pyqtSignal, QAbstractTableModel, QTimer, QObject, pyqtSlot, QEvent, QUrl, QTimer from PyQt5.QtGui import QStandardItemModel, QStandardItem, QPixmap, QGuiApplication, QColor, QKeyEvent from PyQt5.QtWidgets import QApplication, QFileDialog, QTableWidgetItem, QLabel, QMainWindow, QTableView, QVBoxLayout, QWidget, QComboBox, QMessageBox, QGraphicsScene, QGraphicsPixmapItem, QDialogButtonBox, QInputDialog, QMessageBox, QTabWidget, QSpinBox from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile, QWebEnginePage, QWebEngineSettings from PyQt5.QtWebChannel import QWebChannel # 이 줄을 추가하세요. #QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts) now = datetime.now() class CustomSpinBox(QSpinBox): def __init__(self, delta=1000, parent=None): super(CustomSpinBox, self).__init__(parent) self.delta = delta self.setSingleStep(self.delta) def textFromValue(self, value): # 3자리 숫자마다 콤마를 찍는 로직 return "{:,}".format(value) class PandasModel(QAbstractTableModel): def __init__(self, data): QAbstractTableModel.__init__(self) self._data = data def rowCount(self, parent=None): return self._data.shape[0] def columnCount(self, parent=None): return self._data.shape[1] def data(self, index, role): if not index.isValid(): return None # 배경색 변경 if role == Qt.BackgroundColorRole: matching_url = self._data.iloc[index.row()]['MatchingUrl'] if matching_url and matching_url.startswith('http'): return QColor('lightgray') if self.selected_cell == (index.row(), index.column()): # 선택된 셀 return QColor('yellow') # 폰트 변경 if role == Qt.FontRole: current_row = index.row() current_keyword_id = self._data.iloc[current_row, 1] # None 값 처리 if current_keyword_id is None: current_keyword_id = "" previous_keyword_id = "" if current_row > 0: previous_keyword_id = self._data.iloc[current_row - 1, 1] if previous_keyword_id is None: previous_keyword_id = "" # 첫 번째 행이거나 이전 행보다 keyword_id가 큰 경우 폰트 스타일 변경 if current_row == 0 or current_keyword_id > previous_keyword_id: font = QtGui.QFont() font.setBold(True) font.setUnderline(True) return font # 기타 데이터 처리 if role == Qt.DisplayRole: return str(self._data.iloc[index.row(), index.column()]) return None def headerData(self, section, orientation, role): if role == Qt.DisplayRole: if orientation == Qt.Horizontal: return self._data.columns[section] else: return str(self._data.index[section]) return None def selected_cell(self, cell): self.selected_cell = cell self.layoutChanged.emit() # 뷰 업데이트 def update_selected_cell(self, cell): self.selected_cell = cell self.layoutChanged.emit() # 뷰 업데이트 class CustomWebEnginePage(QWebEnginePage): def __init__(self, profile, parent=None, use_mobile=False): super(CustomWebEnginePage, self).__init__(profile, parent) self.mobile_user_agent = "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1" self.desktop_user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" self.setZoomFactor(0.75) # 줌을 75%로 설정 #self.use_mobile_agent(False) #self.profile().setHttpUserAgent(mobile_user_agent) if use_mobile: self.profile().setHttpUserAgent(self.mobile_user_agent) else: self.profile().setHttpUserAgent(self.desktop_user_agent) # JavaScript 활성화 settings = self.settings() settings.setAttribute(QWebEngineSettings.JavascriptEnabled, True) settings.setAttribute(QWebEngineSettings.JavascriptCanOpenWindows, True) settings.setAttribute(QWebEngineSettings.JavascriptCanAccessClipboard, True) def use_mobile_agent(self, use_mobile): if use_mobile: self.profile().setHttpUserAgent(self.mobile_user_agent) else: self.profile().setHttpUserAgent(self.desktop_user_agent) self.parent().load(QUrl("https://world.taobao.com/wow/tmg-fc/tmw/search_image?spm=")) def event(self, event): if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Backspace: self.triggerAction(QWebEnginePage.Back) return True return super().event(event) def contextMenuEvent(self, event): menu = self.createStandardContextMenu() menu.addAction('Open Inspector', self.inspect) menu.exec_(event.globalPos()) def inspect(self): self.triggerAction(QWebEnginePage.InspectElement) class CustomWebEngineView(QWebEngineView): def createWindow(self, window_type): # tabWidget 인스턴스에 접근 logging.info("createWindow 호출됨") tab_widget = self.parent().findChild(QTabWidget, "tabWidget") # 새 창 요청을 처리할 webEngineView_2 인스턴스에 접근 new_web_view = tab_widget.findChild(QWebEngineView, "webEngineView_2") # 새 창 요청을 webEngineView_2로 전달하고, tab_2를 활성화 if tab_widget and new_web_view: logging.info("새 창 요청 처리 - tab_2 활성화") tab_widget.setCurrentIndex(1) # tab_2를 활성화 return new_web_view else: logging.info("tabWidget 또는 webEngineView_2를 찾을 수 없음") return super(CustomWebEngineView, self).createWindow(window_type) def keyPressEvent(self, event): if event.key() == Qt.Key_Backspace: self.back() # 백스페이스가 눌리면 이전 페이지로 이동 else: super().keyPressEvent(event) # # 새 창 요청을 처리하는 로직 # self.parent().findChild(QTabWidget, "tabWidget").setCurrentWidget(self.parent().findChild(QWidget, "tab_2")) # return self.parent().findChild(QWebEngineView, "webEngineView_2") #class NewWebEnginePage(QWebEnginePage): # def __init__(self, parent=None): # super(NewWebEnginePage, self).__init__(parent) class Ui_Dialog(QtWidgets.QDialog): def __init__(self): super(Ui_Dialog, self).__init__() uic.loadUi('outline8.ui', self) logging.info("Ui_Dialog 초기화 시작") # db_name 속성 초기화 #self.db_name = None # 여기서 초기값을 None 또는 기본 DB 파일 경로로 설정합니다. # db_name 속성에 기본값 설정 self.db_name = 'baseDB.db' # 기본 DB 파일 이름. 필요에 따라 변경하세요. # 로그 설정 # logging.basicConfig(filename='taoseller.log', level=logging.INFO) # 로그 설정 logger = logging.getLogger() logger.setLevel(logging.INFO) # RotatingFileHandler를 사용하여 로그 파일 설정 log_handler = RotatingFileHandler(filename='taoseller.log', maxBytes=1024, backupCount=3) log_handler.setLevel(logging.INFO) # 로그 포매터 설정 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') log_handler.setFormatter(formatter) # 로거에 핸들러 추가 logger.addHandler(log_handler) # 클래스 변수 self.current_table_name = 'Keywords' self.selected_row_id = None self.model = None # model 속성 초기화 self.count_test1 = 0 self.count_test2 = 0 self.matching_urlss = "" self.matching_urls = "" self.papago_client_id ="" self.papago_client_secret = "" self.endMatchingUrl = None self.automatchBtn.clicked.connect(self.automatch_clicked) self.img_searchbtn.clicked.connect(self.img_search) self.Excel_btn.clicked.connect(self.openxls) self.matchbtn.clicked.connect(self.matchbtn_clicked) # matchbtn 버튼이 클릭되면 matchbtn_clicked 메소드 호출 #self.ns_scraping.clicked.connect(self.on_ns_scraping_clicked) self.webEngineView.urlChanged.connect(self.update_url) self.dbviewer1.clicked.connect(self.on_table_clicked) self.sc_button.clicked.connect(self.on_button_clicked) # Qt Designer에서 생성한 UI 클래스 내에서 버튼 클릭 이벤트 연결 self.papagoApi.clicked.connect(self.manage_papago_api_keys) # 프로그램 초기화 부분에 API 키 관리 함수 호출 self.manage_papago_api_keys() # CustomWebEnginePage 설정 #profile = QWebEngineProfile.defaultProfile() # 기본 프로필 사용 # webEngineView에 CustomWebEnginePage 설정 #self.webEngineView.setPage(CustomWebEnginePage(profile, self.webEngineView)) #self.webEngineView = CustomWebEngineView(self.webEngineView) # webEngineView_2에도 CustomWebEnginePage 설정 (필요한 경우) #self.webEngineView_2.setPage(CustomWebEnginePage(profile, self.webEngineView_2)) #self.webEngineView_2 = CustomWebEngineView(self.webEngineView_2) # webEngineView 인스턴스를 CustomWebEngineView로 초기화 #self.webEngineView = CustomWebEngineView(self.tabWidget) # 부모를 tabWidget으로 설정 # webEngineView_2 인스턴스를 CustomWebEngineView로 초기화 #self.webEngineView_2 = CustomWebEngineView(self.tabWidget) # 부모를 tabWidget으로 설정 # 초기 탭 설정 self.tabWidget.setCurrentIndex(0) # 첫 번째 탭을 활성화 # 페이지 설정 profile = QWebEngineProfile.defaultProfile() # webEngineView에는 모바일 에이전트 설정 self.webEngineView.setPage(CustomWebEnginePage(profile, self.webEngineView, use_mobile=True)) logging.info("webEngineView 페이지 설정 완료") # webEngineView_2에는 PC 에이전트 설정 self.webEngineView_2.setPage(CustomWebEnginePage(profile, self.webEngineView_2, use_mobile=False)) logging.info("webEngineView_2 페이지 설정 완료") # webEngineView와 webEngineView_2 초기화 #self.webEngineView = CustomWebEngineView(self) #self.webEngineView_2 = CustomWebEngineView(self) # CustomWebEnginePage 설정 #profile = QWebEngineProfile.defaultProfile() #self.webEngineView.setPage(CustomWebEnginePage(profile, self.webEngineView)) #self.webEngineView_2.setPage(CustomWebEnginePage(profile, self.webEngineView_2)) # 스위치 UI 요소 (예: 버튼)에 이벤트 연결 self.switchBtn.clicked.connect(self.toggleUserAgent) # 기본 URL 설정 및 로드 "https://papago.naver.net/website?source=zh-CN&target=ko&locale=ko&url=https://world.taobao.com" #default_url = "https://world.taobao.com" default_url = "https://main.m.taobao.com" default_url2 = "https://world.taobao.com/wow/tmg-fc/tmw/search_image?" self.webEngineView.load(QUrl(default_url)) self.webEngineView_2.load(QUrl(default_url2)) # 기존DB불러오기 버튼 클릭이벤트 self.db_btn.clicked.connect(self.loadExistingDb) # db를 엑셀로 저장 버튼 self.export_btn.clicked.connect(self.export_data) self.export_btn_by_auto.clicked.connect(self.export_data_by_auto) # 불러와진 DB에서 테이블을 읽어서 콤보박스에 표시하기 self.comboBox.currentIndexChanged.connect(self.loadTable) # Load database and table names #self.db_name = '202312250341.db' # Replace with your database name self.loadTableNames() # Load database and table names #self.current_table_name = None # 현재 테이블 이름을 저장할 변수 # QGraphicsScene 초기화 self.scene = QGraphicsScene(self) #self.current_img self.graphicsView_multi.setScene(self.scene) # QGraphicsView의 정렬을 상단 및 좌측에 맞추기 self.graphicsView_multi.setAlignment(Qt.AlignTop) # 새로운 QGraphicsView 및 QGraphicsScene 초기화 self.scene_current_img = QGraphicsScene(self) self.current_img.setScene(self.scene_current_img) # searchbtn 버튼 클릭 이벤트 연결 #self.searchbtn.clicked.connect(self.on_searchbtn_clicked) self.searchbtn_2.clicked.connect(self.search_chinese) self.search_keyword_box.returnPressed.connect(self.search_chinese) # urlbox와 urlgobtn 이벤트 연결 self.urlbox.returnPressed.connect(self.load_url_from_urlbox) self.urlgobtn.clicked.connect(self.load_url_from_urlbox) # 타이머 설정 self.timer = QTimer(self) self.timer.timeout.connect(self.updateTimer) # 시작, 정지, 설정 버튼 self.timerStart.clicked.connect(self.startTimer) self.timerStop.clicked.connect(self.stopTimer) self.timerSet.clicked.connect(self.setTimer) self.startTimer() # 타이머 시간 표시 QLabel #self.timerLabel = QLabel('60:00', self)P # 타이머 시간 self.remainingTime = 3600 # 초 단위 (60분) # 기존 QSpinBox 객체를 CustomSpinBox 객체로 변환 self.delivfee_spbox = self.findChild(QSpinBox, 'delivfee_spbox') self.addpackingfee_spbox = self.findChild(QSpinBox, 'addpackingfee_spbox') self.addmargin_spbox = self.findChild(QSpinBox, 'addmargin_spbox') # QSpinBox 객체를 CustomSpinBox로 설정 self.updateSpinBox(self.delivfee_spbox, 1000) self.updateSpinBox(self.addpackingfee_spbox, 1000) self.updateSpinBox(self.addmargin_spbox, 1000) # 각 QSpinBox의 단일 스텝 설정 # self.delivfee_spbox.setSingleStep(delta_delivfee) # self.addpackingfee_spbox.setSingleStep(delta_addpackingfee) # self.addmargin_spbox.setSingleStep(delta_addmargin) def automatch_clicked(self): logging.info("automatch_clicked") item_count =10 sort_order = 1 logging.info(f"automatch_item_count : {item_count}, sort_order : {sort_order}") QMessageBox.information(self, "알림", "타오바오 파싱 중 생성되는 크름브라우저를 종료하지 마세요\n 완료 메세지가 생성될때 까지 [로그인요구], [캡차해결] 이외에는 조작하지 마세요") automatch(self.db_name, item_count, sort_order, logging) QMessageBox.information(self, "알림", "타오바오 파싱이 완료되었습니다.") return # img_search 함수 구현 def img_search(self): # 웹 페이지 로드 img_search__url = "https://world.taobao.com/wow/tmg-fc/tmw/search_image?" #self.tab_widget.setCurrentIndex(1) # tab_2를 활성화 self.tabWidget.setCurrentIndex(2) # 2 번째 탭을 활성화 self.webEngineView_2.load(QUrl(img_search__url)) logging.info("웹페이지 로드") # 페이지 로드가 완료될 때까지 대기 (필요에 따라 시간 조정) QTimer.singleShot(500, self.paste_and_search_image) def paste_and_search_image(self): # QWebEngineView에 포커스를 맞춥니다 self.webEngineView_2.setFocus() logging.info("포커스이동") # CSS 선택자로 요소 찾아 클릭 click_input_js = """ var inputElement = document.querySelector('.rax-textinput'); if (inputElement) { inputElement.click(); } """ self.webEngineView_2.page().runJavaScript(click_input_js) logging.info("자바클릭:검색창클릭") # 클립보드에서 이미지 붙여넣기 # 시간 지연을 주어 웹 페이지가 준비될 수 있도록 합니다 time.sleep(0.1) # Ctrl+V 키보드 단축키를 시뮬레이션하여 이미지를 붙여넣습니다 pyautogui.hotkey('ctrl', 'v') logging.info("컨트롤 브이 실행") # 이미지 붙여넣기 후 버튼 클릭 (추가 대기 시간 필요) QTimer.singleShot(100, self.click_search_button) def click_search_button(self): # .component-preview-button 클릭 click_search_js1 = """ console.log('클릭 스크립트 시작'); var searchButton = document.querySelector('.component-preview-button'); if (searchButton) { searchButton.click(); } """ click_search_js = """ console.log('클릭 스크립트 시작'); // .component-preview-button 요소가 나타날 때까지 1초 간격으로 확인 function waitForButton() { var searchButton = document.querySelector('.component-preview-button'); if (searchButton) { console.log('버튼 찾음'); searchButton.click(); } else { console.log('버튼 찾지 못함. 0.3초 후 다시 확인'); setTimeout(waitForButton, 600); } } waitForButton(); """ self.webEngineView_2.page().runJavaScript(click_search_js) logging.info("사진검색 버튼 클릭") # 이미지 붙여넣기 후 버튼 클릭 (추가 대기 시간 필요) # QTimer.singleShot(3500, self.login) def login(self): # 로그인 정보 tao_id = self.tao_id tao_pw = self.tao_pw logging.info("로그인 실행") # 로그인 스크립트 login_script = """ console.log('로그인 스크립트 시작'); var loginFrame = document.querySelector('#baxia-dialog-content'); if (loginFrame) { console.log('로그인 프레임 발견'); var loginDocument = loginFrame.contentDocument || loginFrame.contentWindow.document; setTimeout(function() { console.log('iframe 내부 탐색 시작'); var idField = loginDocument.querySelector('#fm-login-id'); var pwField = loginDocument.querySelector('#fm-login-password'); var loginButton = loginDocument.querySelector('.fm-button'); if (idField && pwField && loginButton) { console.log('로그인 필드 발견'); idField.value = '""" + tao_id + """'; pwField.value = '""" + tao_pw + """'; loginButton.click(); console.log('로그인 시도'); } else { console.log('로그인 필드를 찾을 수 없음'); } }, 3000); // iframe이 로드될 때까지 기다림 } else { console.log('로그인 프레임을 찾을 수 없음'); } """ # 로그인 스크립트 실행 self.webEngineView_2.page().runJavaScript(login_script) logging.info("로그인 실행 완료") def updateSpinBox(self, spinBox, step): # QSpinBox 객체의 기능 확장 if spinBox: spinBox.setSingleStep(step) spinBox.textFromValue = lambda value: "{:,}".format(value) def toggleUserAgent(self): current_page = self.webEngineView_2.page() # 현재 페이지를 가져옵니다. # 현재 사용 중인 사용자 에이전트가 모바일 버전인지 확인 is_mobile = current_page.profile().httpUserAgent() == current_page.mobile_user_agent # 사용자 에이전트 전환 #self.webEnginePage.use_mobile_agent(not is_mobile) current_page.use_mobile_agent(not is_mobile) # 페이지 새로고침 (변경 사항 적용을 위해) -> class단에서 이미지검색url로 이동처리 #self.webEngineView.reload() def manage_papago_api_keys(self): api_keys_filename = 'papago_api_keys.txt' current_client_id = "" current_client_secret = "" if os.path.exists(api_keys_filename): with open(api_keys_filename, 'r') as file: lines = file.readlines() if len(lines) >= 2: # 각 행에 대한 존재 확인 및 처리 current_client_id_line = lines[0] current_client_secret_line = lines[1] if ':' in current_client_id_line: current_client_id = current_client_id_line.split(':')[1].strip().strip('"') self.papago_client_id = current_client_id_line.split(':')[1].strip().strip('"') if ':' in current_client_secret_line: current_client_secret = current_client_secret_line.split(':')[1].strip().strip('"') self.papago_client_secret = current_client_secret_line.split(':')[1].strip().strip('"') if not current_client_id or not current_client_secret: reply = QMessageBox.question(self, "API 키 설정", "API 키가 없습니다. 설정하시겠습니까?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: self.get_papago_api_keys_from_user(api_keys_filename) else: msg = QMessageBox() msg.setIcon(QMessageBox.Question) msg.setWindowTitle("Papago API Keys") msg.setText(f"현재 API Keys:\nClient ID: {current_client_id}\nClient Secret: {current_client_secret}\n\nAPI키를 바꿀까요?") msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) if msg.exec_() == QMessageBox.Yes: self.get_papago_api_keys_from_user(api_keys_filename) else: self.get_papago_api_keys_from_user(api_keys_filename) def get_papago_api_keys_from_user(self, api_keys_filename): client_id, ok1 = QInputDialog.getText(self, 'Papago API', 'Enter Client ID:') client_secret, ok2 = QInputDialog.getText(self, 'Papago API', 'Enter Client Secret:') if ok1 and ok2: with open(api_keys_filename, 'w') as file: file.write(f'ClientID: "{client_id}"\nClientSecret: "{client_secret}"\n') self.papago_client_id = client_id self.papago_client_secret = client_secret QMessageBox.information(self, "알림", "Papago API 설정이 완료되었습니다.") def translate_with_papago(self, text): url = "https://openapi.naver.com/v1/papago/n2mt" headers = { "X-Naver-Client-Id": self.papago_client_id, "X-Naver-Client-Secret": self.papago_client_secret, } data = { "source": "ko", "target": "zh-CN", "text": text, } response = requests.post(url, headers=headers, data=data) if response.status_code == 200: translated_text = response.json().get('message', {}).get('result', {}).get('translatedText', '') logging.info(f"키워드변경 : {text} -> {translated_text}") return translated_text else: # 오류 처리 및 사용자에게 메시지 표시 error_message = "번역 중 오류 발생" if response.status_code == 401: error_message = "파파고 API 키 인증 오류: 잘못된 API 키 또는 API키가 없습니다." elif response.status_code == 429: error_message = "파파고 API 최대 호출량(20000/Day)을 초과했습니다." elif response.status_code == 500: error_message = "서버 내부 오류가 발생했습니다." # 오류 메시지 표시 QMessageBox.warning(self, "오류", error_message) return None #@pyqtSlot(QObject) def on_button_clicked(self, button): if self.sc_button.buttonRole(button) == QDialogButtonBox.ApplyRole: self.on_ns_scraping_clicked() def startTimer(self): # QLineEdit에서 시간 가져오기 minutes = int(self.timerMinute.text()) seconds = int(self.timerSecond.text()) self.remainingTime = minutes * 60 + seconds self.timer.start(1000) self.startMatchingUrl = self.getMatchingUrlCount() def stopTimer(self): self.timer.stop() self.endMatchingUrl = self.getMatchingUrlCount() work_done = self.endMatchingUrl - self.startMatchingUrl QMessageBox.information(self, "정지", f"잠시 중지: 현재작업량 = {work_done}") def setTimer(self): self.startTimer() def updateTimer(self): self.remainingTime -= 1 if self.remainingTime <= 0: self.timer.stop() endMatchingUrl = self.getMatchingUrlCount() work_done = self.endMatchingUrl - self.startMatchingUrl QMessageBox.information(self, "시간 완료", f"1시간이 지났습니다. 작업효율은 시간당 {work_done}개입니다.") self.remainingTime = 3600 minutes, seconds = divmod(self.remainingTime, 60) self.timerLabel.setText(f"{minutes:02d}:{seconds:02d}") def getMatchingUrlCount(self): # 데이터베이스 연결 및 MatchingUrl 개수 조회 try: conn = sqlite3.connect(self.db_name) # 데이터베이스 파일 이름을 self.db_name 변수에 저장 current_table = self.comboBox.currentText() # 현재 선택된 테이블 이름 가져오기 query = f"SELECT COUNT(*) FROM {current_table} WHERE MatchingUrl IS NOT NULL" cursor = conn.cursor() cursor.execute(query) count = cursor.fetchone()[0] # 첫 번째 결과(카운트) 가져오기 conn.close() return count except Exception as e: logging.info(f"Error: {e}") return 0 # 에러가 발생한 경우 0 반환 def load_url_from_urlbox(self): url = self.urlbox.text() if url: self.webEngineView.load(QUrl(url)) def loadTableNames(self): conn = sqlite3.connect(self.db_name) cursor = conn.cursor() cursor.execute("SELECT name FROM sqlite_master WHERE type='table';") tables = cursor.fetchall() conn.close() self.comboBox.clear() # 기존에 콤보박스에 있는 항목들을 클리어 logging.info("테이블 목록 읽기") for table in tables: self.comboBox.addItem(table[0]) logging.info(f"테이블 {table} 읽기") if tables: self.comboBox.setCurrentIndex(0) # 첫 번째 테이블 선택 self.current_table_name = tables[0][0] # 첫 번째 테이블 이름을 current_table_name에 저장 self.loadTable() # 첫 번째 테이블 로드 self.update_match_count() def loadTable_ori(self): table_name = self.comboBox.currentText() self.current_table_name = table_name # 현재 테이블 이름 저장 #if table_name: # conn = sqlite3.connect(self.db_name) # df = pd.read_sql(f"SELECT * FROM {table_name}", conn) # conn.close() # model = PandasModel(df) # self.dbviewer1.setModel(model) if table_name: conn = sqlite3.connect(self.db_name) df = pd.read_sql(f"SELECT * FROM {table_name}", conn) conn.close() self.model = PandasModel(df) self.dbviewer1.setModel(self.model) # 셀 선택 변경 이벤트 처리 self.dbviewer1.selectionModel().selectionChanged.connect(self.cell_selected) self.update_match_count() def cell_selected(self, selected, deselected): if selected.indexes(): selected_index = selected.indexes()[0] row, column = selected_index.row(), selected_index.column() self.model.update_selected_cell((row, column)) self.update_match_count() def export_data(self): # 저장할 파일 위치와 이름 선택 today = datetime.now().strftime('%Y%m%d_%H%M') default_filename = f"{today}_1.xlsx" file_name, _ = QFileDialog.getSaveFileName(self, "엑셀 파일 저장", default_filename, "Excel Files (*.xlsx)") if file_name: #self.save_to_excel(file_name) self.save_to_excel_with_xlwings(self.db_name, file_name) def export_data_by_auto(self): # 저장할 파일 위치와 이름 선택 today = datetime.now().strftime('%Y%m%d_%H%M') default_filename = f"{today}_1.xlsx" file_name, _ = QFileDialog.getSaveFileName(self, "엑셀 파일 저장", default_filename, "Excel Files (*.xlsx)") if file_name: #self.save_to_excel(file_name) self.save_to_excel_with_xlwings_by_auto(self.db_name, file_name) def save_to_excel_success(self, file_name): # 기존 엑셀 파일 복사 base_filename = "baseXLS.xlsx" shutil.copy(base_filename, file_name) logging.info(f"파일 '{base_filename}'이 '{file_name}'으로 복사되었습니다.") # 데이터베이스에서 데이터 로드 conn = sqlite3.connect(self.db_name) current_table = self.comboBox.currentText() query = f"SELECT * FROM {current_table} WHERE MatchingUrl IS NOT NULL" df = pd.read_sql_query(query, conn) conn.close() # 복사된 파일에 새로운 데이터 추가 with pd.ExcelWriter(file_name, engine='openpyxl', mode='a', if_sheet_exists='replace') as writer: # 새로운 데이터를 'export' 시트에 추가 (시트가 없으면 생성) startrow = writer.book['export'].max_row if 'export' in writer.book.sheetnames else 0 df.to_excel(writer, sheet_name='export', startrow=startrow, header=False, index=False) logging.info(f"파일 '{file_name}'에 데이터가 추가되었습니다.") def save_to_excel_ori2(self, file_name): # 데이터베이스에서 데이터 로드 conn = sqlite3.connect(self.db_name) current_table = self.comboBox.currentText() query = f"SELECT * FROM {current_table} WHERE MatchingUrl IS NOT NULL" df = pd.read_sql_query(query, conn) conn.close() # 50개 행씩 나누어 파일 저장 for i in range(0, len(df), 50): df_subset = df.iloc[i:i+50] # 부분 파일 이름 생성 part_file_name = file_name.replace('.xlsx', f'_part{i//50 + 1}.xlsx') # 기존 엑셀 파일 복사 base_filename = "baseXLS.xlsx" shutil.copy(base_filename, part_file_name) logging.info(f"파일 '{base_filename}'이 '{part_file_name}'으로 복사되었습니다.") # 복사된 파일에 새로운 데이터 추가 with pd.ExcelWriter(part_file_name, engine='openpyxl', mode='a', if_sheet_exists='replace') as writer: # 새로운 데이터를 'export' 시트에 추가 (시트가 없으면 생성) startrow = writer.book['export'].max_row if 'export' in writer.book.sheetnames else 0 df_subset.to_excel(writer, sheet_name='export', startrow=startrow, header=False, index=False) # 'multi_ss' 시트에서 모든 함수 제거 if 'multi_ss' in writer.book.sheetnames: logging.info(f"시트 multi_ss를 찾았습니다.") ws = writer.book['multi_ss'] for row in ws.iter_rows(): for cell in row: if cell.value is not None and isinstance(cell.value, str) and cell.value.startswith('='): cell.value = cell.value # Replace formula with its value logging.info(f"셀 '{cell}'에 함수를 제거하였습니다.") logging.info(f"파일 '{part_file_name}'에 데이터가 추가되었습니다.") writer._save #writer.close() def save_to_excel(self, file_name): # 데이터베이스에서 필요한 데이터 로드 conn = sqlite3.connect(self.db_name) query = "SELECT MatchingUrl, keyword, MatchingCat, delvFee, packingFee, plusFee, manuTag FROM NaverShopping WHERE MatchingUrl IS NOT NULL" df = pd.read_sql_query(query, conn) conn.close() # 'manuTag' 필드에서 '오늘' 단어 제거 df['manuTag'] = df['manuTag'].apply(lambda x: ','.join([word for word in str(x).split(',') if '오늘' not in word.strip()])) # 50개 행씩 나누어 파일 저장 for i in range(0, len(df), 50): df_subset = df.iloc[i:i+50] part_file_name = file_name.replace('.xlsx', f'_part{i//50 + 1}.xlsx') # 기존 엑셀 파일 복사 base_filename = "baseXLS_Percenty.xlsx" shutil.copy(base_filename, part_file_name) logging.info(f"파일 '{base_filename}'이 '{part_file_name}'으로 복사되었습니다.") # 엑셀 파일에 데이터 쓰기 #book = load_workbook(part_file_name) #writer = pd.ExcelWriter(part_file_name, engine='openpyxl') #writer.book = book # 기존 워크북을 로드 book = load_workbook(part_file_name) writer = pd.ExcelWriter(part_file_name, engine='openpyxl') writer.book = book #writer.sheets = {ws.title:ws for ws in book.worksheets} #df.to_excel(writer, index = False, header=False, startrow=writer.sheets['Sheet1'].max_row) if 'multi_ss' in book.sheetnames: ws = book['multi_ss'] # 각 필드의 데이터를 지정된 셀에 입력 for index, row in df_subset.iterrows(): row_num = 4 + (index % 50) ws[f'B{row_num}'] = row['MatchingUrl'] ws[f'C{row_num}'] = row['keyword'] ws[f'G{row_num}'] = row['MatchingCat'] ws[f'E{row_num}'] = row['delvFee'] + row['packingFee'] ws[f'D{row_num}'] = row['plusFee'] ws[f'F{row_num}'] = row['manuTag'] # 변경 사항 저장 writer.save() writer.close() logging.info(f"파일 '{part_file_name}'에 데이터가 추가되었습니다.") def save_to_excel_with_xlwings(self, db_name, file_name): # 로그 다이얼로그 생성 및 표시 log_dialog = LogDialog() log_dialog.show() self.saved_files = [] logging.info("엑셀 저장 로그기록 시작") message = "엑셀 저장 로그기록 시작" log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가 # 로그 설정 logging.basicConfig(filename='app.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s') logging.warning('This will get logged to a file') logging.info("엑셀 저장 로그기록 파일 생성") # 데이터베이스에서 필요한 데이터 로드 conn = sqlite3.connect(db_name) query = "SELECT MatchingUrl, keyword, MatchingCat, delvFee, packingFee, plusFee, manuTag FROM NaverShopping WHERE MatchingUrl IS NOT NULL" df = pd.read_sql_query(query, conn) conn.close() logging.info("DB 읽기 완료") message = "DB 읽기 완료" log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가 # 'manuTag' 필드에서 '오늘' 단어 제거 df['manuTag'] = df['manuTag'].apply(lambda x: ','.join([word for word in str(x).split(',') if '오늘' not in word.strip()])) logging.info("태그 필터링") message = "태그 필터링" log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가 # Excel 애플리케이션을 백그라운드에서 실행 app = xw.App(visible=False) logging.info("xlwings 시작") try: # 50개 행씩 나누어 파일 저장 for i in range(0, len(df), 50): df_subset = df.iloc[i:i+50] # 기존 엑셀 파일 복사 base_filename = "baseXLS_Percenty.xlsx" shutil.copy(base_filename, file_name) logging.info("저장양식 엑셀파일 열기") message = "저장양식 엑셀파일 열기" log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가 # 복사된 파일 열기 wb = xw.Book(file_name) ws = wb.sheets['multi_ss'] # 데이터 삽입 for index, row in df_subset.iterrows(): row_num = 4 + (index % 50) ws.range(f'B{row_num}').value = row['MatchingUrl'] ws.range(f'C{row_num}').value = row['keyword'] ws.range(f'G{row_num}').value = row['MatchingCat'] ws.range(f'E{row_num}').value = row['delvFee'] + row['packingFee'] ws.range(f'D{row_num}').value = row['plusFee'] ws.range(f'F{row_num}').value = row['manuTag'] logging.info(f"{index}번째 {row_num-3}엑셀데이터 기록") # 저장 및 닫기 logging.info("파일저장시작") part_file_name = file_name.replace('.xlsx', f'_part{i//50 + 1}.xlsx') wb.api.SaveCopyAs(part_file_name) #wb.save(part_file_name) # 저장된 파일의 이름을 self.saved_files 리스트에 추가 self.saved_files.append(part_file_name) logging.info("파일저장완료 및 워크북 닫기 시작") wb.close() logging.info("워크북 닫기 완료") logging.info(f"파일 '{part_file_name}'에 데이터가 추가되었습니다.") message = f"파일 '{part_file_name}'에 데이터가 추가되었습니다." log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가 time.sleep(1) # 로그에 파일 저장 정보 추가 logging.info(f"로그 '{{part_file_name.log}}'에 로그데이터가 추가되었습니다.") except Exception as e: logging.info(e) # 예외를 로그에 기록 logging.error(f"파일 저장 중 예외 발생: {str(e)}") message = f"파일 저장 중 예외 발생: {str(e)}" log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가 finally: # self.prompt_to_open_files() #QMessageBox.information(self, "알림", "저장 프로세스가 완료되었습니다.") #def prompt_to_open_files(self): reply = QMessageBox.question(None, '완료', "저장 프로세스가 완료되었습니다. 저장된 엑셀 파일이 있는 폴더를 열어보시겠습니까?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: if self.saved_files: folder_path = os.path.dirname(self.saved_files[0]) os.startfile(folder_path) else: logging.info("저장된 파일이 없습니다.") else: logging.info("저장 프로세스가 종료되었습니다.") def save_to_excel_with_xlwings_by_auto(self, db_name, file_name): # 로그 다이얼로그 생성 및 표시 log_dialog = LogDialog() log_dialog.show() self.saved_files = [] logging.info("엑셀 저장 로그기록 시작") message = "엑셀 저장 로그기록 시작" log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가 # 로그 설정 logging.basicConfig(filename='app.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s') logging.warning('This will get logged to a file') logging.info("엑셀 저장 로그기록 파일 생성") # 데이터베이스에서 필요한 데이터 로드 conn = sqlite3.connect(db_name) query = """ SELECT NS.* FROM NaverShopping NS INNER JOIN ( SELECT keyword_id, MIN(id) AS MinId FROM NaverShopping WHERE MatchingUrl IS NOT NULL GROUP BY keyword_id ) AS UniqueNS ON NS.keyword_id = UniqueNS.keyword_id AND NS.id = UniqueNS.MinId """ df = pd.read_sql_query(query, conn) conn.close() logging.info("DB 읽기 완료") logging.info(f"선택된 쿼리에 의한 데이터프레임 \n {df}") message = "DB 읽기 완료" log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가 # 'manuTag' 필드에서 '오늘' 단어 제거 df['manuTag'] = df['manuTag'].apply(lambda x: ','.join([word for word in str(x).split(',') if '오늘' not in word.strip()])) logging.info("태그 필터링") message = "태그 필터링" log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가 # Excel 애플리케이션을 백그라운드에서 실행 app = xw.App(visible=False) logging.info("xlwings 시작") try: # 50개 행씩 나누어 파일 저장 for i in range(0, len(df), 50): df_subset = df.iloc[i:i+50] # 기존 엑셀 파일 복사 base_filename = "baseXLS_Percenty.xlsx" shutil.copy(base_filename, file_name) logging.info("저장양식 엑셀파일 열기") message = "저장양식 엑셀파일 열기" log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가 # 복사된 파일 열기 wb = xw.Book(file_name) ws = wb.sheets['multi_ss'] # 데이터 삽입 for index, row in df_subset.iterrows(): row_num = 4 + (index % 50) ws.range(f'B{row_num}').value = row['MatchingUrl'] ws.range(f'C{row_num}').value = row['keyword'] ws.range(f'G{row_num}').value = row['MatchingCat'] ws.range(f'E{row_num}').value = row['delvFee'] + row['packingFee'] ws.range(f'D{row_num}').value = row['plusFee'] ws.range(f'F{row_num}').value = row['manuTag'] logging.info(f"{index}번째 {row_num-3}엑셀데이터 기록") # 저장 및 닫기 logging.info("파일저장시작") part_file_name = file_name.replace('.xlsx', f'_part{i//50 + 1}.xlsx') wb.api.SaveCopyAs(part_file_name) #wb.save(part_file_name) # 저장된 파일의 이름을 self.saved_files 리스트에 추가 self.saved_files.append(part_file_name) logging.info("파일저장완료 및 워크북 닫기 시작") wb.close() logging.info("워크북 닫기 완료") logging.info(f"파일 '{part_file_name}'에 데이터가 추가되었습니다.") message = f"파일 '{part_file_name}'에 데이터가 추가되었습니다." log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가 time.sleep(1) # 로그에 파일 저장 정보 추가 logging.info(f"로그 '{{part_file_name.log}}'에 로그데이터가 추가되었습니다.") except Exception as e: logging.info(e) # 예외를 로그에 기록 logging.error(f"파일 저장 중 예외 발생: {str(e)}") message = f"파일 저장 중 예외 발생: {str(e)}" log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가 finally: # self.prompt_to_open_files() #QMessageBox.information(self, "알림", "저장 프로세스가 완료되었습니다.") #def prompt_to_open_files(self): reply = QMessageBox.question(None, '완료', "저장 프로세스가 완료되었습니다. 저장된 엑셀 파일이 있는 폴더를 열어보시겠습니까?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: if self.saved_files: folder_path = os.path.dirname(self.saved_files[0]) os.startfile(folder_path) else: logging.info("저장된 파일이 없습니다.") else: logging.info("저장 프로세스가 종료되었습니다.") def loadExistingDb(self): db_file, _ = QFileDialog.getOpenFileName(self, "Open Database", "", "Database Files (*.db)") if db_file: self.db_name = db_file #self.loadDataFromDb(db_file) self.loadDataFromDb(self.db_name, "NaverShopping") self.update_match_count() # def loadExistingDb(self): # # 파일 선택 대화상자를 통해 .db 파일 선택 # db_file, _ = QFileDialog.getOpenFileName(self, "Open Database", "", "Database Files (*.db)") # if db_file: # self.loadDataFromDb(db_file) # #self.load_data_into_table_NaverShopping(db_file) def get_query_for_table_ori(self, table_name): # 각 테이블별 다른 쿼리 실행 if table_name == "Keywords": return "SELECT keyword, base_category, MatchingUrl, MatchingCat FROM Keywords" elif table_name == "NaverShopping": return "SELECT keyword_id, keyword, delvFee, packingFee, plusFee, MatchingUrl, MatchingCat, productTitle, price, category1Name, category2Name, category3Name, category4Name FROM NaverShopping" elif table_name == "Taobao": return "SELECT keyword_id, item_name, price, imageSearchUrl, keywordSearchUrl, rank FROM Taobao" elif table_name == "SubKeywords": return "SELECT keyword_id, relatedTags FROM SubKeywords" elif table_name == "Matching": return "SELECT keyword_id, naver_id, taobao_id, sub_keyword_id FROM Matching" else: return "SELECT * FROM " + table_name # 기본값 또는 예외 처리 def get_query_for_table(self, table_name): queries = { "Keywords": "SELECT keyword, base_category, MatchingUrl, MatchingCat FROM Keywords", "NaverShopping": "SELECT keyword_id, keyword, delvFee, packingFee, plusFee, MatchingUrl, MatchingCat, productTitle, price, category1Name, category2Name, category3Name, category4Name, localImagePath, id, cat_code, tao_imageUrl FROM NaverShopping", "Taobao": "SELECT keyword_id, item_name, price, imageSearchUrl, keywordSearchUrl, rank FROM Taobao", "SubKeywords": "SELECT keyword_id, relatedTags FROM SubKeywords", "Matching": "SELECT keyword_id, naver_id, taobao_id, sub_keyword_id FROM Matching" } return queries.get(table_name, "Invalid table name") def loadDataFromDb(self, db_file, table_name): query = self.get_query_for_table(table_name) logging.info(f"loadDataFromDb_table_name : {table_name}") if query == "Invalid table name": logging.info("Invalid table name: " + table_name) # 여기에 추가적인 오류 처리 또는 사용자에게 알림을 제공하는 코드를 추가할 수 있습니다. return # 데이터베이스에서 데이터 로드 connection = sqlite3.connect(db_file) df = pd.read_sql(query, connection) connection.close() # PandasModel로 데이터 설정 self.model = PandasModel(df) self.dbviewer1.setModel(self.model) # 셀 선택 변경 이벤트 연결 self.dbviewer1.selectionModel().selectionChanged.connect(self.cell_selected) self.update_match_count() def loadDataFromDb_ori3(self, db_file): table_name = self.comboBox.currentText() query = self.get_query_for_table(table_name) # 데이터베이스에서 데이터 로드 및 모델 설정 connection = sqlite3.connect(db_file) df = pd.read_sql(query, connection) connection.close() self.model = PandasModel(df) self.dbviewer1.setModel(self.model) self.model.setHorizontalHeaderLabels(df.columns.values.tolist()) self.update_match_count() def loadTable_ori2(self): table_name = self.comboBox.currentText() self.current_table_name = table_name self.loadDataFromDb(self.db_name) self.dbviewer1.selectionModel().selectionChanged.connect(self.cell_selected) def loadTable(self): table_name = self.comboBox.currentText() logging.info(f"loadTable_table_name : {table_name}") self.current_table_name = table_name self.loadDataFromDb(self.db_name, table_name) self.dbviewer1.selectionModel().selectionChanged.connect(self.cell_selected) def loadDataFromDb_ori2(self, db_file): # 선택된 테이블 이름 가져오기 selected_table = self.comboBox.currentText() # 데이터베이스 연결 connection = sqlite3.connect(db_file) # 테이블별로 다른 쿼리 실행 if selected_table == "Keywords": query = "SELECT keyword, base_category, MatchingUrl, MatchingCat FROM Keywords" elif selected_table == "NaverShopping": query = "SELECT keyword, delvFee, packingFee, plusFee, MatchingUrl, MatchingCat, productTitle, price, category1Name, category2Name, category3Name, category4Name FROM NaverShopping" elif selected_table == "Taobao": query = "SELECT keyword_id, item_name, price, imageSearchUrl, keywordSearchUrl, rank FROM Taobao" elif selected_table == "SubKeywords": query = "SELECT keyword_id, relatedTags FROM SubKeywords" elif selected_table == "Matching": query = "SELECT keyword_id, naver_id, taobao_id, sub_keyword_id FROM Matching" else: query = "SELECT * FROM " + selected_table # 기본값 # SQL 쿼리 실행 및 결과 가져오기 result = pd.read_sql_query(query, connection) # 결과를 QStandardItemModel에 로드 model = QStandardItemModel() for row in range(result.shape[0]): for col in range(result.shape[1]): item = QStandardItem(str(result.iloc[row, col])) model.setItem(row, col, item) # QTableView 설정 self.dbviewer1.setModel(model) model.setHorizontalHeaderLabels(result.columns.values.tolist()) # 열 이름 설정 # 데이터베이스 연결 종료 connection.close() self.update_match_count() def loadDataFromDb_ori(self, db_file): # 데이터베이스에서 데이터를 로드하여 QTableView에 표시 connection = sqlite3.connect(db_file) cursor = connection.cursor() cursor.execute("SELECT * FROM NaverShopping") rows = cursor.fetchall() query = "SELECT * FROM NaverShopping" # Keywords 테이블에서 모든 데이터를 선택하는 쿼리문 result = pd.read_sql_query(query, connection) # 쿼리문 실행하고 결과를 pandas DataFrame으로 반환 model = QStandardItemModel() for row in range(result.shape[0]): # 각 행에 대해 for col in range(result.shape[1]): # 각 열에 대해 item = QStandardItem(str(result.iloc[row, col])) # 데이터를 QStandardItem로 변환 model.setItem(row, col, item) # 모델에 아이템 설정 # QTableView 설정 self.dbviewer1.setModel(model) model.setHorizontalHeaderLabels(result.columns.values.tolist()) # 열 이름 설정 self.dbviewer1.hideColumn(0) # 첫 번째 열(ID 열)을 숨김 connection.close() self.update_match_count() # searchbtn 클릭 이벤트 처리 함수 def on_searchbtn_clicked(self): current_index = self.dbviewer1.currentIndex() # 현재 선택된 행의 인덱스 chinese_keyword = current_index.sibling(current_index.row(), 33).data() # 33번째 열에서 중국어 키워드 가져오기 if chinese_keyword: # 타오바오 검색 URL 생성 및 로드 "https://papago.naver.net/website?source=zh-CN&target=ko&locale=ko&url=https://world.taobao.com" search_url = f"https://main.m.taobao.com/search/index.html?pagetype=1&q={chinese_keyword}" #search_url = f"https://papago.naver.net/website?source=zh-CN&target=ko&locale=ko&url=https://main.m.taobao.com/search/index.html?pagetype=1&q={chinese_keyword}" self.webEngineView.load(QUrl(search_url)) def search_chinese(self): #current_index = self.dbviewer1.currentIndex() # 현재 선택된 행의 인덱스 # 현재 선택된 행의 키워드 가져오기 #keyword = current_index.sibling(current_index.row(), 2).data() search_keyword = self.search_keyword_box.text() self.tabWidget.setCurrentIndex(0) # tab_1을 활성화 # 키워드 번역 translated_keyword = self.translate_with_papago(search_keyword) if translated_keyword: # 타오바오 검색 URL 생성 및 로드 https://papago.naver.net/website?source=zh-CN&target=ko&locale=ko&url= search_url = f"https://main.m.taobao.com/search/index.html?pagetype=1&q={translated_keyword}" #search_url = f"https://papago.naver.net/website?source=zh-CN&target=ko&locale=ko&url=https://main.m.taobao.com/search/index.html?pagetype=1&q={translated_keyword}" self.webEngineView.load(QUrl(search_url)) # catcodebox 값을 확인하고 배경색을 설정하는 함수 def check_and_set_color(self): if not self.catcodebox.text(): # catcodebox 값이 비어있는 경우 self.catcodebox.setStyleSheet("QLineEdit { background-color: red; }") else: self.catcodebox.setStyleSheet("") # 기본 스타일로 초기화 def on_table_clicked(self, index): # 선택된 행의 키워드 가져오기 keyword = index.sibling(index.row(), 1).data() try: cat1 = index.sibling(index.row(), 9).data() logging.info(f"카테고리1 = '{cat1}'선택됨.") cat2 = index.sibling(index.row(), 10).data() logging.info(f"카테고리2 = '{cat2}'선택됨.") cat3 = index.sibling(index.row(), 11).data() logging.info(f"카테고리3 = '{cat3}'선택됨.") cat4 = index.sibling(index.row(), 12).data() logging.info(f"카테고리4 = '{cat4}'선택됨.") cat_code = index.sibling(index.row(), 15).data() logging.info(f"카테고리 코드 = '{cat_code}'선택됨.") # 카테고리 값들을 합치기 categories = [cat1, cat2, cat3] if cat4: # category4Name이 비어있지 않은 경우에만 추가 categories.append(cat4) # '-'를 사용하여 카테고리 이름들을 연결 combined_category = '-'.join(categories) final_category = cat_code + " " + combined_category logging.info(f"최종 카테고리 = '{final_category}' ") # 카테고리 박스에 선택된 사항 표시 self.catbox1.setText(cat1) self.catbox2.setText(cat2) self.catbox3.setText(cat3) self.catbox4.setText(cat4) self.catcodebox.setText(cat_code) except Exception as e: logging.info(f"카테고리 오류 : {e}") # 카테고리 코드 박스가 비어있을 경우 붉은색으로 경고표시 self.check_and_set_color() # 키워드박스에 선택된 키워드 표시 self.selkeywordbox.setText(keyword) self.search_keyword_box.setText(keyword) # 선택된 행에서 이미지 경로 가져오기 localImagePath = index.sibling(index.row(), 13).data() tao_image = index.sibling(index.row(), 15).data() self.selected_image_path = localImagePath logging.info(f"이미지 경로 '{localImagePath}'선택됨.") # 클립보드에 이미지 복사 try: if localImagePath: clipboard = QtGui.QGuiApplication.clipboard() image = QtGui.QImage(localImagePath) if not image.isNull(): clipboard.setImage(image) logging.info(f"이미지 '{localImagePath}'가 클립보드에 복사되었습니다.") self.load_image_from_clipboard(localImagePath) # 이미지선택칸에 현재 이미지표시 # self.tabWidget.setCurrentIndex(1) # tab_2를 활성화 else: logging.info(f"이미지 로드 실패: {localImagePath}") except Exception as e: logging.info(f"이미지 클립보드 복사 오류: {e}") # 선택된 행의 ID 가져오기 (ID가 첫 번째 열에 있다고 가정) self.selected_row_id = index.sibling(index.row(), 14).data() # 선택된 행의 keyword_id 가져오기 keyword_id = index.sibling(index.row(), 0).data() self.load_images_by_keyword_id(keyword_id) logging.info(f"선택된 keyword_id: {keyword_id}") # keyword_id에 해당하는 레코드들을 찾아서 처리 try: logging.info(f"DB 연결 시도: {self.db_name}") conn = sqlite3.connect(self.db_name) cursor = conn.cursor() query = f"SELECT productTitle, manuTag, reviewCount, keepCnt, dlvryLowPrice, purchaseCnt FROM {self.current_table_name} WHERE keyword_id = ? ORDER BY id LIMIT 5" #logging.info(f"실행할 쿼리: {query}") #cursor.execute(f"SELECT * FROM {self.current_table_name} WHERE keyword_id = ? ORDER BY some_ordering_column", (keyword_id,)) cursor.execute(query, (keyword_id,)) records = cursor.fetchall() #logging.info(f"가져온 레코드: {records}") # 연관 태그를 가져오는 쿼리 #cursor.execute(f"SELECT relatedTags FROM {self.current_table_name} WHERE keyword_id = ? LIMIT 1", (keyword_id,)) cursor.execute(f"SELECT relatedTags FROM {self.current_table_name} WHERE keyword_id = ?", (keyword_id,)) relatedTags_records = cursor.fetchall() # 연관 태그 처리 relatedTags_list = [tag for record in relatedTags_records for tag in record if tag] relatedTags = ', '.join(relatedTags_list) if relatedTags_list else "없음" logging.info(f"가져온 연관검색어 레코드: {relatedTags_records}") conn.close() # sel_relatedTagbox에 연관 태그 설정 self.sel_relatedTagbox.setText(relatedTags) self.sel_relatedTagbox.setCursorPosition(0) # 상품별 필드값 설정 manuTags = ["" for _ in range(5)] # 5개 상품의 manuTag 초기화 logging.info(f"manuTags : {manuTags}") if not records: logging.info("해당 keyword_id에 대한 레코드가 없습니다.") return # 레코드 처리 # 모든 관련 QLineEdit 위젯 클리어 #for k in range(1, 6): # for l in range(1, 6): # getattr(self, f'sel_itembox{i+1}{j}').clear() for i, record in enumerate(records): # 모든 관련 QLineEdit 위젯 클리어 for j in range(1, 7): getattr(self, f'sel_itembox{i+1}{j}').clear() # 레코드의 각 요소를 변수에 할당, 값이 없는 경우 "0" 또는 ""으로 설정 productTitle = str(record[0]) if len(record) > 0 else "0" manuTag = str(record[1]) if len(record) > 1 else "" reviewCount = str(record[2]) if len(record) > 2 else "0" keepCnt = str(record[3]) if len(record) > 3 else "0" dlvryLowPrice = "{:,}".format(int(record[4])) if len(record) > 4 and record[4] is not None else "0" purchaseCnt = str(record[5]) if len(record) > 5 else "0" # 위젯에 값 설정 getattr(self, f'sel_itembox{i+1}1').setText(productTitle) getattr(self, f'sel_itembox{i+1}2').setText(manuTag) getattr(self, f'sel_itembox{i+1}3').setText(reviewCount) getattr(self, f'sel_itembox{i+1}4').setText(keepCnt) getattr(self, f'sel_itembox{i+1}5').setText(dlvryLowPrice) getattr(self, f'sel_itembox{i+1}6').setText(purchaseCnt) # 모든 QLineEdit의 커서를 맨 왼쪽으로 설정 for j in range(1, 7): getattr(self, f'sel_itembox{i+1}{j}').setCursorPosition(0) # manuTag 누적 #manuTags[i] = manuTag # manuTag 처리: 튜플이면 문자열로 변환 #if isinstance(manuTag, tuple): # manuTag = ' '.join(map(str, manuTag)) # 튜플을 공백으로 구분된 문자열로 변환 #elif manuTag is None: # manuTag = "" #manuTags[i] += manuTag # 각 상품 정보 설정 #getattr(self, f'sel_itembox{i+1}1').setText(productTitle) #getattr(self, f'sel_itembox{i+1}3').setText(str(reviewCount)) #getattr(self, f'sel_itembox{i+1}4').setText(str(keepCnt)) #getattr(self, f'sel_itembox{i+1}5').setText(str(dlvryLowPrice)) #getattr(self, f'sel_itembox{i+1}6').setText(str(purchaseCnt)) # 모든 상품의 manuTag 설정 #for i in enumerate(records): #getattr(self, f'sel_itembox{i+1}2').setText(manuTags[i]) except sqlite3.OperationalError as e: logging.info(f"SQL 오류: {e}") QtWidgets.QMessageBox.warning(self, "오류", "DB에서 데이터를 가져오는 중 오류가 발생했습니다.") except Exception as e: logging.info(f"일반 오류: {e}") QtWidgets.QMessageBox.warning(self, "오류", "알 수 없는 오류가 발생했습니다.") self.update_match_count() def download_and_save_image(self, url, save_dir, image_name): # 파일 이름에 타임스탬프 추가하여 고유한 이름 생성 timestamp = datetime.now().strftime("%Y%m%d%H%M%S") unique_image_name = f"{timestamp}_{image_name}" try: response = requests.get(url) if response.status_code == 200: # 이미지 데이터를 BytesIO 객체로 변환 image_data = BytesIO(response.content) image = Image.open(image_data) # 이미지 크기 조정 #image = image.resize((150, 150), Image.ANTIALIAS) image = image.resize((150, 150), Image.Resampling.LANCZOS) # 이미지 저장 디렉토리 확인 및 생성 if not os.path.exists(save_dir): os.makedirs(save_dir) # 이미지 저장 경로 설정 file_path = os.path.join(save_dir, unique_image_name) # Convert the image to RGB rgb_image = image.convert('RGB') # 이미지 저장 rgb_image.save(file_path, format='JPEG') return file_path else: logging.info(f"Failed to download image: {url}") return None except Exception as e: logging.info(f"Error downloading image {url}: {e}") return None def load_images_by_keyword_id(self, keyword_id): logging.info(keyword_id) try: conn = sqlite3.connect(self.db_name) cursor = conn.cursor() # 이미지 URL 대신 로컬 이미지 경로를 불러옵니다. cursor.execute(f"SELECT localImagePath FROM {self.current_table_name} WHERE keyword_id = ? LIMIT 5", (keyword_id,)) image_paths = cursor.fetchall() conn.close() logging.info("로컬 이미지 경로 로드완료") logging.info(image_paths) self.load_images([path[0] for path in image_paths if path[0]]) except sqlite3.OperationalError as e: logging.info(e) QtWidgets.QMessageBox.warning(self, "오류", "DB의 유효한 테이블을 선택해주세요.") def load_images(self, image_paths): self.scene.clear() y_position = 0 for path in image_paths: pixmap = QPixmap(path) resized_pixmap = pixmap.scaled(150, 150, Qt.KeepAspectRatio, Qt.SmoothTransformation) pixmap_item = QGraphicsPixmapItem(resized_pixmap) pixmap_item.setPos(0, y_position) self.scene.addItem(pixmap_item) y_position += resized_pixmap.height() + 10 def load_image_from_clipboard(self,img_path): self.scene_current_img.clear() pixmap = QPixmap(img_path) if not pixmap.isNull(): view_size = self.current_img.viewport().size() scaled_pixmap = pixmap.scaled(view_size, Qt.KeepAspectRatio, Qt.SmoothTransformation) pixmap_item = QGraphicsPixmapItem(scaled_pixmap) self.scene_current_img.addItem(pixmap_item) else: self.scene_current_img.clear() logging.info("클립보드에 이미지가 없습니다.") def load_images_by_keyword_id_ori(self, keyword_id): logging.info(keyword_id) try: conn = sqlite3.connect(self.db_name) cursor = conn.cursor() cursor.execute(f"SELECT imageUrl FROM {self.current_table_name} WHERE keyword_id = ? LIMIT 5", (keyword_id,)) image_urls = cursor.fetchall() conn.close() logging.info("이미지URL 로드완료") logging.info(image_urls) self.load_images([url[0] for url in image_urls if url[0]]) except sqlite3.OperationalError as e: logging.info(e) QtWidgets.QMessageBox.warning(self, "오류", "DB의 유효한 테이블을 선택해주세요.") def load_images_ori(self, image_urls): self.scene.clear() y_position = 0 for url in image_urls: try: response = requests.get(url) if response.status_code == 200: pixmap = QPixmap() if pixmap.loadFromData(response.content): resized_pixmap = pixmap.scaled(150, 150, Qt.KeepAspectRatio, Qt.SmoothTransformation) pixmap_item = QGraphicsPixmapItem(resized_pixmap) pixmap_item.setPos(0, y_position) self.scene.addItem(pixmap_item) y_position += resized_pixmap.height() + 10 else: logging.info(f"Cannot load image from data: {url}") else: logging.info(f"Failed to download image from {url}, Status code: {response.status_code}") except Exception as e: logging.info(f"Error loading image from {url}: {e}") def update_url_for_match(self, q): url_str = q # 정규 표현식을 사용하여 12자리 숫자 찾기 match = re.search(r'id=(\d{10,12})', url_str) if match: # 정규 표현식으로 찾은 ID를 사용하여 PC 주소 생성 pc_url = f"https://item.taobao.com/item.htm?id={match.group(1)}" else: # id 매개변수가 없는 경우 원래 URL을 사용 pc_url = url_str # 사용자에게 메시지 띄우기 QMessageBox.information(self, "알림", "상품URL을 확인하세요!") return pc_url def matchbtn_clicked(self): # 현재 활성화된 탭의 인덱스 가져오기 current_tab_index = self.tabWidget.currentIndex() # 현재 활성화된 탭에 따라 URL 가져오기 if current_tab_index == 0: # 첫 번째 탭 (tab_1)이 활성화된 경우 matching_url = self.webEngineView.url().toString() elif current_tab_index == 1: # 두 번째 탭 (tab_2)이 활성화된 경우 matching_url = self.webEngineView_2.url().toString() elif current_tab_index == 2: # 두 번째 탭 (tab_2)이 활성화된 경우 matching_url = self.webEngineView_2.url().toString() logging.info(f"current_tab_index : {current_tab_index}") logging.info(f"matching_url : {matching_url}") #matchbtn 버튼이 클릭되면, 입력한 값들을 DB에서 현재 선택된 키워드에 해당하는 필드에 기록 packing_fee = int(self.addpackingfee_spbox.text()) deliv_fee = int(self.delivfee_spbox.text()) add_margin = int(self.addmargin_spbox.text()) table_name = self.comboBox.currentText() #현재는 QLineEdit cat1 = self.catbox1.text() cat2 = self.catbox2.text() cat3 = self.catbox3.text() cat4 = self.catbox4.text() cat_code = self.catcodebox.text() #콤보박스로 바꾸면 쓸 코드 #cat1 = self.cat1combo.currentText() #cat2 = self.cat2combo.currentText() #cat3 = self.cat3combo.currentText() #cat4 = self.cat4combo.currentText() #cat_code = self.catcodecombo.currentText() categories = [cat1, cat2, cat3] if cat4: # cat4가 비어있지 않은 경우에만 추가 categories.append(cat4) # '-'를 사용하여 카테고리 이름들을 연결 combined_category = '-'.join(categories) logging.info(f"최종 카테고리 = '{combined_category}' ") # cat_code와 combined_category를 " "로 연결 final_category = cat_code + " " + combined_category MatchingCat = final_category matching_url = self.update_url_for_match(matching_url) itemtag = self.sel_itemtagbox.text() keyword = self.selkeywordbox.text() #relatedTag = sel_relatedTagbox.text() conn = sqlite3.connect(self.db_name) # DB 연결 cursor = conn.cursor() #tablevier 변경1 df = pd.read_sql(f"SELECT * FROM {table_name}", conn) logging.info(f"선택된 행 : {self.selected_row_id}") #self.selected_row_id = index.sibling(index.row(), 15).data() # 업데이트 쿼리에서 선택된 행의 ID를 사용 if self.selected_row_id is not None: cursor.execute("UPDATE NaverShopping SET MatchingCat =?, delvFee = ?, packingFee = ?, plusFee = ?, MatchingUrl = ?, manuTag = ?, keyword = ? WHERE id = ?", (MatchingCat, deliv_fee, packing_fee, add_margin, matching_url, itemtag, keyword, self.selected_row_id)) conn.commit() conn.close() # DB 연결 종료 self.model = PandasModel(df) self.dbviewer1.setModel(self.model) self.load_data_into_table_NaverShopping() self.update_match_count() self.loadTable() # 셀 선택 변경 이벤트 처리 self.dbviewer1.selectionModel().selectionChanged.connect(self.cell_selected) def update_match_count(self): # 데이터베이스 연결 conn = sqlite3.connect(self.db_name) current_table = self.comboBox.currentText() if current_table: # current_table이 비어 있지 않은 경우에만 쿼리 실행 # MatchingUrl이 있는 행의 수를 카운트 query = f"SELECT COUNT(*) FROM {current_table} WHERE MatchingUrl IS NOT NULL" cursor = conn.cursor() cursor.execute(query) match_count = cursor.fetchone()[0] conn.close() # QLabel에 표시 self.matchCountbox.setText(str(match_count)) else: # current_table이 비어 있는 경우 self.matchCountbox.setText("0") # 또는 적절한 기본값으로 설정 # def on_ns_scraping_clicked_ori(self): # QMessageBox.information(self, "알림", "네이버쇼핑 스크래핑 작업이 시작되었습니다.") # # DB 연결 및 cursor 생성 # self.count_test1 += 1 # logging.info(f"count_test1 = {self.count_test1}") # conn = sqlite3.connect(self.db_name) # 이 부분은 self.db_name이 정의되어 있어야 합니다. # cursor = conn.cursor() # # Keywords 테이블에서 keyword_id와 keyword 가져오기 # cursor.execute("SELECT id, keyword FROM Keywords") # rows = cursor.fetchall() # isOverseas = self.isOverseas.text() # sortcount = self.sortcount.text() # for row in rows: # id = row[0] # keyword = row[1] # logging.info(row[1]) # 각 행의 내용을 출력하여 구조 확인 # parse_naver_shopping(id, keyword, conn, isOverseas, sortcount) # 함수 호출 # self.count_test2 += 1 # logging.info(f"count_test2 = {self.count_test2}") # # DB 연결 종료 # conn.close() # self.imageSave() # #self.trans_chinese() # self.load_data_into_table_NaverShopping() # QMessageBox.information(self, "알림", "스크래핑 완료!") def find_naver_code(self, base_categories): naver_code_set = [] for base_category in base_categories: # ">" 기준으로 분류하여 변수 할당 category_parts = base_category.split('>') base_category1Name, base_category2Name, base_category3Name, base_category4Name = (category_parts + [None]*4)[:4] # # JSON 파일 로드 # with open("Percenty_SS_code.json", "r", encoding='utf-8') as file: # data = json.load(file) # # 조건에 맞는 Naver_code 찾기 # for item in data: # if (item.get('category1Name') == base_category1Name and # item.get('category2Name') == base_category2Name and # item.get('category3Name') == base_category3Name and # item.get('category4Name') == base_category4Name): # naver_code_set.append(item['Naver_code']) # JSON 파일 로드, 파일의 각 줄을 개별 JSON 객체로 처리 with open("Percenty_SS_code.json", "r", encoding='utf-8') as file: for line in file: try: item = json.loads(line) # 조건에 맞는 Naver_code 찾기 if (item.get('category1Name') == base_category1Name and item.get('category2Name') == base_category2Name and item.get('category3Name') == base_category3Name and item.get('category4Name') == base_category4Name): naver_code_set.append(item['Naver_code']) except json.JSONDecodeError as e: logging.info(f"Error decoding JSON: {e}") continue logging.info(naver_code_set) return naver_code_set def on_ns_scraping_clicked(self): #conn = sqlite3.connect(self.db_name) self.conndb = sqlite3.connect(self.db_name, check_same_thread=False) logging.info(f"선택된 DB: {self.db_name}") cursor = self.conndb.cursor() # cursor.execute("SELECT id, keyword FROM Keywords") cursor.execute("SELECT id, keyword, base_category FROM Keywords") rows = cursor.fetchall() keywords = [row[1] for row in rows] # base_category = [row[2] for row in rows] base_categories = [row[2] for row in rows] naver_code = self.find_naver_code(base_categories) self.scraping_thread = ScrapingThread(self.conndb, keywords, naver_code, self.isOverseas.text(), self.sortcount.text()) self.scraping_thread.progress_updated.connect(self.update_progress_bar) # self.scraping_thread.finished.connect(self.on_scraping_finished) # 스크래핑 완료 후 처리 self.scraping_thread.finished.connect(self.start_image_save_thread) # 스크래핑 완료 후 이미지 저장 스레드 시작 self.scraping_thread.start() QMessageBox.information(self, "알림", "네이버쇼핑 스크래핑 시작!") def start_image_save_thread(self): # 이미지 저장 폴더 설정 ('DB이름_save_images') image_save_folder = f"{self.db_name}_save_images" # 폴더가 없으면 생성 if not os.path.exists(image_save_folder): os.makedirs(image_save_folder) # 이미지 저장 스레드 실행 self.image_save_thread = ImageSaveThread(self.conndb, image_save_folder) self.image_save_thread.progress_updated.connect(self.update_image_progress_bar) self.image_save_thread.finished.connect(self.on_image_save_finished) # 이미지 저장 완료 후 처리 self.image_save_thread.start() def on_image_save_finished(self): QMessageBox.information(self, "알림", "스크래핑 완료!") self.conndb.close() # 모든 백그라운드 작업이 끝난 후 데이터베이스 연결 종료 def update_progress_bar(self, total, current): self.sc_progressBar.setMaximum(total) self.sc_progressBar.setValue(current) def update_image_progress_bar(self, total, current): self.img_progressBar.setMaximum(total) self.img_progressBar.setValue(current) def on_ns_scraping_clicked_ori(self): self.scraping_thread = ScrapingThread() self.scraping_thread.progress_updated.connect(self.update_progress_bar) self.scraping_thread.start() QMessageBox.information(self, "알림", "네이버쇼핑 스크래핑 시작!") # DB 연결 및 cursor 생성 self.count_test1 += 1 logging.info(f"count_test1 = {self.count_test1}") conn = sqlite3.connect(self.db_name) cursor = conn.cursor() # Keywords 테이블에서 keyword_id와 keyword 가져오기 cursor.execute("SELECT id, keyword FROM Keywords") rows = cursor.fetchall() # ProgressBar 설정 self.sc_progressBar.setMaximum(len(rows)) self.sc_progressBar.setValue(0) isOverseas = self.isOverseas.text() sortcount = self.sortcount.text() for idx, row in enumerate(rows): id = row[0] keyword = row[1] logging.info(row[1]) # 각 행의 내용을 출력하여 구조 확인 parse_naver_shopping(id, keyword, conn, isOverseas, sortcount) # 함수 호출 self.count_test2 += 1 logging.info(f"count_test2 = {self.count_test2}") # ProgressBar 업데이트 self.sc_progressBar.setValue(idx + 1) # DB 연결 종료 conn.close() self.imageSave() #self.trans_chinese() self.load_data_into_table_NaverShopping() QMessageBox.information(self, "알림", "스크래핑 완료!") def imageSave_ori(self): # 이미지 저장 폴더 설정 ('DB이름_save_images') image_save_folder = f"{self.db_name}_save_images" # DB 연결 및 cursor 생성 conn = sqlite3.connect(self.db_name) # 이 부분은 self.db_name이 정의되어 있어야 합니다. cursor = conn.cursor() # NaverShopping 테이블에서 해당 keyword_id의 imageUrl을 가져옵니다. cursor.execute("SELECT id, keyword, imageUrl FROM NaverShopping") shopping_rows = cursor.fetchall() # ProgressBar 설정 self.img_progressBar.setMaximum(len(shopping_rows)) self.img_progressBar.setValue(0) logging.info(shopping_rows) # 각 행의 내용을 출력하여 구조 확인 #for shopping_row in shopping_rows: for idx, shopping_row in enumerate(shopping_rows): id = shopping_row[0] keyword = shopping_row[1] imageUrl = shopping_row[2] # imageUrl 가져오기 logging.info(imageUrl) if imageUrl: # 이미지 이름을 'keyword_id'와 고유 번호를 이용하여 생성 image_name = f"{keyword}_{id}.jpg" # 이미지 다운로드 및 로컬에 저장 local_image_path = self.download_and_save_image(imageUrl, image_save_folder, image_name) logging.info(local_image_path) # 각 행의 내용을 출력하여 구조 확인 if local_image_path: # 데이터베이스에 로컬 이미지 경로를 업데이트 cursor.execute("UPDATE NaverShopping SET localImagePath = ? WHERE imageUrl = ?", (local_image_path, imageUrl)) # ProgressBar 업데이트 self.img_progressBar.setValue(idx + 1) # 데이터베이스 변경 사항을 커밋하고 연결 종료 # DB 연결 종료 conn.commit() # Commit the transaction conn.close() def trans_chinese(self): # 네이버 API 클라이언트 ID 및 시크릿 client_id = "V1UyIry1TNhzj4ln1UJ7" client_secret = "YV3EsIWlTH" logging.info("중국어번역") # DB 연결 및 cursor 생성 conn = sqlite3.connect(self.db_name) # 이 부분은 self.db_name이 정의되어 있어야 합니다. cursor = conn.cursor() # DB 연결 종료 conn.commit() # Commit the transaction conn.close() # def on_ns_scraping_clicked_file(self): # # 이미지 저장 폴더 설정 ('DB이름_save_images') # image_save_folder = f"{self.db_name}_save_images" # # DB 연결 및 cursor 생성 # conn = sqlite3.connect(self.db_name) # 이 부분은 self.db_name이 정의되어 있어야 합니다. # cursor = conn.cursor() # # Keywords 테이블에서 id와 keyword를 가져옵니다. # cursor.execute("SELECT id, keyword FROM Keywords") # keyword_rows = cursor.fetchall() # isOverseas = self.isOverseas.text() # sortcount = self.sortcount.text() # for keyword_row in keyword_rows: # keyword_id = keyword_row[0] # keyword = keyword_row[1] # # 중국어로 번역 # #chinese_keyword = self.translate_with_papago(keyword) # # 중국어 키워드를 DB에 저장 # #cursor.execute("UPDATE NaverShopping SET chinese_keyword = ? WHERE id = ?", (chinese_keyword, keyword_id)) # parse_naver_shopping(keyword_id, keyword, conn, isOverseas, sortcount) # 함수 호출 # # NaverShopping 테이블에서 해당 keyword_id의 imageUrl을 가져옵니다. # cursor.execute("SELECT id, imageUrl FROM NaverShopping") # shopping_rows = cursor.fetchall() # logging.info(shopping_rows) # 각 행의 내용을 출력하여 구조 확인 # for shopping_row in shopping_rows: # id = shopping_row[0] # imageUrl = shopping_row[1] # imageUrl 가져오기 # logging.info(imageUrl) # if imageUrl: # # 이미지 이름을 'keyword_id'와 고유 번호를 이용하여 생성 # image_name = f"{keyword}_{keyword_id}.jpg" # # 이미지 다운로드 및 로컬에 저장 # local_image_path = self.download_and_save_image(imageUrl, image_save_folder, image_name) # logging.info(local_image_path) # 각 행의 내용을 출력하여 구조 확인 # if local_image_path: # # 데이터베이스에 로컬 이미지 경로를 업데이트 # cursor.execute("UPDATE NaverShopping SET localImagePath = ? WHERE imageUrl = ?", (local_image_path, imageUrl)) # # 데이터베이스 변경 사항을 커밋하고 연결 종료 # # DB 연결 종료 # conn.commit() # Commit the transaction # conn.close() # self.load_data_into_table_NaverShopping() # self.update_match_count() def update_url(self, q): url_str = q.toString() if hasattr(q, 'toString') else q self.urlbox.setText(url_str) # 정규 표현식을 사용하여 11자리 또는 12자리 숫자 찾기 match = re.search(r'id=(\d{10,12})', url_str) if match: # 정규 표현식으로 찾은 ID를 사용하여 PC 주소 생성 pc_url = f"https://item.taobao.com/item.htm?id={match.group(1)}" else: pc_url = url_str # ID가 없는 경우 원래 URL을 사용 # 사용자에게 메시지 띄우기 # QMessageBox.information(self, "알림", "상품URL을 확인하세요!") # 결과 URL 업데이트 self.taourlbox.setText(pc_url) def openxls(self): options = QFileDialog.Options() file_name, _ = QFileDialog.getOpenFileName(None,"QFileDialog.getOpenFileName()", "","Excel Files (*.xlsx)", options=options) if file_name: # DB 파일 이름 설정 now = datetime.now() self.db_name = now.strftime('%Y-%m-%d-%H%M') + '.db' conn = sqlite3.connect(self.db_name) # DB 생성 및 데이터 로드 create_db(self.db_name) df = pd.read_excel(file_name, usecols=['키워드', '카테고리']) df.columns = ['keyword', 'base_category'] df.to_sql('Keywords', conn, if_exists='append', index=False) conn.close() read_excel_category_data(self.db_name) # DB 로드 후 테이블 이름을 콤보박스에 로드하고 첫 번째 테이블 선택 self.loadTableNames() def load_data_into_table_keyword(self): conn = sqlite3.connect(self.db_name) # DB 연결 query = "SELECT * FROM Keywords" # Keywords 테이블에서 모든 데이터를 선택하는 쿼리문 result = pd.read_sql_query(query, conn) # 쿼리문 실행하고 결과를 pandas DataFrame으로 반환 # 결과를 QTableView에 표시 model = QStandardItemModel() for row in range(result.shape[0]): # 각 행에 대해 for col in range(result.shape[1]): # 각 열에 대해 item = QStandardItem(str(result.iloc[row, col])) # 데이터를 QStandardItem로 변환 model.setItem(row, col, item) # 모델에 아이템 설정 model.setHorizontalHeaderLabels(result.columns.values.tolist()) # 열 이름 설정 self.dbviewer1.setModel(model) # QTableView에 모델 설정 self.dbviewer1.hideColumn(0) # 첫 번째 열(ID 열)을 숨김 conn.close() # DB 연결 종료 self.model = QStandardItemModel() # model 속성 설정 self.update_match_count() def load_data_into_table_NaverShopping(self): conn = sqlite3.connect(self.db_name) # DB 연결 query = "SELECT * FROM NaverShopping" # Keywords 테이블에서 모든 데이터를 선택하는 쿼리문 result = pd.read_sql_query(query, conn) # 쿼리문 실행하고 결과를 pandas DataFrame으로 반환 # 결과를 QTableView에 표시 model = QStandardItemModel() for row in range(result.shape[0]): # 각 행에 대해 for col in range(result.shape[1]): # 각 열에 대해 item = QStandardItem(str(result.iloc[row, col])) # 데이터를 QStandardItem로 변환 model.setItem(row, col, item) # 모델에 아이템 설정 model.setHorizontalHeaderLabels(result.columns.values.tolist()) # 열 이름 설정 self.dbviewer1.setModel(model) # QTableView에 모델 설정 self.dbviewer1.hideColumn(0) # 첫 번째 열(ID 열)을 숨김 conn.close() # DB 연결 종료 self.update_match_count() def load_data_into_table_NS(self): conn = sqlite3.connect(self.db_name) # DB 연결 query = "SELECT * FROM Navershooping" # Keywords 테이블에서 모든 데이터를 선택하는 쿼리문 result = pd.read_sql_query(query, conn) # 쿼리문 실행하고 결과를 pandas DataFrame으로 반환 # 결과를 QTableView에 표시 model = QStandardItemModel() for row in range(result.shape[0]): # 각 행에 대해 for col in range(result.shape[1]): # 각 열에 대해 item = QStandardItem(str(result.iloc[row, col])) # 데이터를 QStandardItem로 변환 model.setItem(row, col, item) # 모델에 아이템 설정 model.setHorizontalHeaderLabels(result.columns.values.tolist()) # 열 이름 설정 self.dbviewer1.setModel(model) # QTableView에 모델 설정 self.dbviewer1.hideColumn(0) # 첫 번째 열(ID 열)을 숨김 conn.close() # DB 연결 종료 self.model = QStandardItemModel() # model 속성 설정 self.update_match_count() #if __name__ == "__main__": # app = QApplication([]) # window = Ui_Dialog() # window.show() # app.exec_() if __name__ == "__main__": QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts) app = QApplication(sys.argv) # 프로파일 및 페이지 생성 #profile = QWebEngineProfile("CustomProfile") #mobile_user_agent = "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1" #profile.setHttpUserAgent(mobile_user_agent) window = Ui_Dialog() window.show() sys.exit(app.exec_())