2109 lines
92 KiB
Python
2109 lines
92 KiB
Python
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_()) |