안녕하세요, 초보 개발자 여러분! 😊 오늘은 Python과 PySide6를 이용해 GUI(그래픽 사용자 인터페이스)를 만드는 방법에 대해 알려드리려고 해요. PySide6는 GUI 애플리케이션을 쉽게 만들 수 있는 프레임워크로, 초보 개발자도 금방 익숙해질 수 있답니다. 이번 예제에서는 블로그 제목을 추출하는 간단한 프로그램을 만들어볼 거예요.
[네이버 블로그 제목 추출하기 사이드 프로젝트 한눈에 보기] Python PySide로 네이버 블로그 제목 추출하는 프로그램 만들기
지난번시간에 만든 제목 추출하는 코드를 사용할거에요 !
[네이버 블로그 제목 추출하기 #01] Python requests get 요청 (feat. BeautifulSoup4)
1. 프로젝트 초기 설정하기
먼저 PySide6를 사용하기 위해 필요한 패키지를 설치해야 해요. Python을 설치한 후, 터미널이나 명령 프롬프트에서 다음 명령어를 입력해 PySide6와 필요한 라이브러리를 설치할 수 있어요.
pip install PySide6 requests beautifulsoup4 pandas
이제 필요한 패키지를 모두 설치했으니, Python 코드에서 사용할 수 있어요.
2. GUI 구성하기
이제 본격적으로 GUI를 구성해볼게요. PySide6에서는 QWidget
클래스를 상속받아 사용자 인터페이스를 만들 수 있어요.
코드에서 볼 수 있듯이, QVBoxLayout
과 QHBoxLayout
을 사용해 레이아웃을 구성하고 있어요. 이를 통해 사용자에게 키워드를 입력받고, 추출할 제목의 개수를 선택할 수 있도록 할 거예요.
class TitleExtractionApp(QWidget): def __init__(self): super().__init__() self.initUI()
이 부분은 클래스 생성자에서 UI 초기화를 위한 initUI
메서드를 호출하는 모습이에요. 이제 initUI
메서드에서 실제 UI 요소들을 배치하고 기능을 연결해 볼게요.
3. 키워드 입력 필드와 결과 리스트 구성하기
GUI 프로그램에서 가장 기본적인 요소는 사용자로부터 입력을 받는 필드와, 결과를 보여주는 리스트에요. 이를 위해 키워드 입력 필드와 결과 리스트를 추가해볼게요.
class TitleExtractionApp(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): # 메인 레이아웃 main_layout = QVBoxLayout() # 키워드 입력 필드 추가 keyword_layout = QHBoxLayout() keyword_label = QLabel("키워드:") self.keyword_input = QLineEdit() keyword_layout.addWidget(keyword_label) keyword_layout.addWidget(self.keyword_input) # 리스트 박스 그룹 list_group_box = QGroupBox() list_layout = QVBoxLayout() # 결과 리스트 self.result_list = QListWidget() list_layout.addWidget(self.result_list) list_group_box.setLayout(list_layout) # 오른쪽 위젯들 그룹 right_group_box = QGroupBox() right_layout = QVBoxLayout() # 스핀박스 spin_layout = QHBoxLayout() spin_label = QLabel("추출할 항목 수") self.spin_box = QSpinBox() self.spin_box.setValue(5) spin_layout.addWidget(spin_label) spin_layout.addWidget(self.spin_box) # 추출 시작 버튼 start_button = QPushButton("추출 시작") start_button.clicked.connect(self.extract_titles) # 엑셀 저장 버튼 save_button = QPushButton("엑셀 저장") save_button.clicked.connect(self.save_to_excel) # '이런 기능이 있으면 좋겠어요' 버튼 추가 suggestion_label = QLabel("추가기능 제안하기") feedback_button = QPushButton("[기능 제안하기]\n이런 기능이 있으면 좋겠어요 ") feedback_button.clicked.connect(self.open_feedback_url) # '최신 무료 프로그램 소식 받기' 버튼 추가 program_news_button = QPushButton("최신 무료 프로그램 소식 받기") program_news_button.clicked.connect(self.open_program_news_url) # 레이아웃에 위젯 추가 right_layout.addLayout(spin_layout) right_layout.addWidget(start_button) right_layout.addWidget(save_button) right_layout.addWidget(feedback_button) # 새로운 버튼 추가 right_layout.addWidget(program_news_button) # 새로운 버튼 추가 right_group_box.setLayout(right_layout) # 그리드 레이아웃으로 왼쪽과 오른쪽 배치 grid_layout = QGridLayout() grid_layout.addWidget(list_group_box, 0, 0) grid_layout.addWidget(right_group_box, 0, 1) main_layout.addLayout(keyword_layout) main_layout.addLayout(grid_layout) self.setLayout(main_layout) self.setWindowTitle("제목 추출 프로그램") self.setGeometry(300, 300, 600, 400)
이 코드에서는 키워드를 입력받기 위한 필드를 만들었어요. QLineEdit
을 사용해 사용자가 키워드를 입력할 수 있게 해줍니다. QListWidget
을 사용해 추출된 블로그 제목을 보여주는 리스트를 만들었어요.
4. PySide6 스타일 설정하기
GUI 애플리케이션의 사용자 경험을 높이기 위해서는 스타일을 설정하는 것이 중요해요. PySide6에서는 setStyleSheet
메서드를 사용해 쉽게 스타일을 변경할 수 있어요. 아래 코드는 전체적인 스타일을 설정하는 부분이에요.
class TitleExtractionApp(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): # 기존 코드들 ... self.setStyleSheet(""" QWidget { font-family: Arial, sans-serif; background-color: #f4f4f4; color: #333; } QLabel { font-size: 14px; font-weight: bold; } QGroupBox { border: 1px solid #ccc; border-radius: 8px; padding: 10px; margin-top: 10px; background-color: #ffffff; } QLineEdit { padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; } QSpinBox { padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; } QPushButton { padding: 10px 15px; font-size: 14px; background-color: #FFC107; color: white; border: none; border-radius: 4px; } QPushButton:hover { background-color: #E0A800; } QListWidget { padding: 10px; border: 1px solid #ccc; border-radius: 8px; background-color: #ffffff; } QHBoxLayout, QVBoxLayout { margin: 5px; } QGridLayout { padding: 10px; } """)
이 코드를 통해 GUI의 전반적인 색상과 폰트, 위젯들의 스타일을 설정할 수 있어요.
5. 버튼 클릭 이벤트 연결하기
이제 사용자가 버튼을 클릭했을 때 실행될 동작을 설정해볼게요. PySide6에서는 버튼 클릭 이벤트를 clicked.connect
를 통해 메서드에 연결할 수 있어요.
class TitleExtractionApp(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): #... 기존 코드들 ... # 추출 시작 버튼 start_button = QPushButton("추출 시작") start_button.clicked.connect(self.extract_titles) # 엑셀 저장 버튼 save_button = QPushButton("엑셀 저장") save_button.clicked.connect(self.save_to_excel) # '이런 기능이 있으면 좋겠어요' 버튼 추가 suggestion_label = QLabel("추가기능 제안하기") feedback_button = QPushButton("[기능 제안하기]\n이런 기능이 있으면 좋겠어요 ") feedback_button.clicked.connect(self.open_feedback_url) # '최신 무료 프로그램 소식 받기' 버튼 추가 program_news_button = QPushButton("최신 무료 프로그램 소식 받기") program_news_button.clicked.connect(self.open_program_news_url) # 레이아웃에 위젯 추가 right_layout.addLayout(spin_layout) right_layout.addWidget(start_button) right_layout.addWidget(save_button) right_layout.addWidget(feedback_button) # 새로운 버튼 추가 right_layout.addWidget(program_news_button) # 새로운 버튼 추가 right_group_box.setLayout(right_layout)
6. 블로그 제목 추출 로직 작성하기
사용자가 “추출 시작” 버튼을 클릭하면, 해당 키워드로 네이버 블로그에서 제목을 가져오는 로직을 구현할 거예요. 이를 위해 requests
와 BeautifulSoup
을 사용해 네이버 검색 결과를 크롤링해올 거예요.
def get_naver_titles(keyword, number): ret_titles = [] url = f"https://search.naver.com/search.naver?ssc=tab.blog.all&sm=tab_jum&query={keyword}" try: response = requests.get(url) response.raise_for_status() soup = BeautifulSoup(response.content, 'html.parser') titles = soup.find_all('a', class_='title_link') for i, title in enumerate(titles[:number]): ret_titles.append(f"{i+1}. {title.get_text()}") except Exception as e: ret_titles.append(f"오류 발생: {str(e)}") return ret_titles
이 함수는 네이버 블로그에서 제목을 가져오는 역할을 해요. requests
로 검색 결과를 받아와서, BeautifulSoup
으로 HTML을 파싱하여 제목을 추출합니다.
7. 엑셀로 저장하기
제목을 추출한 후에는 이를 엑셀 파일로 저장할 수 있어요. PySide6에서 QFileDialog
를 사용해 파일 저장 경로를 선택하고, pandas
를 이용해 데이터를 엑셀로 저장하는 방법을 구현해볼게요.
def save_to_excel(self): titles = [self.result_list.item(i).text() for i in range(self.result_list.count())] if not titles: QMessageBox.warning(self, "저장 오류", "저장할 내용이 없습니다.") return file_dialog = QFileDialog() file_path, _ = file_dialog.getSaveFileName(self, "엑셀 파일로 저장", "", "Excel Files (*.xlsx)") if file_path: df = pd.DataFrame({"Titles": titles}) df.to_excel(file_path, index=False) QMessageBox.information(self, "저장 완료", "엑셀 파일로 저장되었습니다.")
이 코드는 추출된 제목을 엑셀 파일로 저장하는 기능을 담당해요. QFileDialog
로 파일 경로를 선택하고, pandas
를 이용해 데이터를 엑셀 파일로 변환한답니다.
프로그램 최종 UI 코드
여기까지, Python과 PySide6를 이용해 GUI 프로그램을 만드는 과정을 함께 살펴보았어요. 이번 프로젝트를 통해 PySide6의 다양한 기능과 사용법을 익히셨기를 바라요. 앞으로도 다양한 기능을 추가하며 더 멋진 프로그램을 만들어보세요!
이 프로그램을 통해 저희 바이너리워커도 PySide6의 강력함과 유용성을 다시금 실감할 수 있었답니다.😊
import sys import requests from bs4 import BeautifulSoup from PySide6.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QHBoxLayout, QSpinBox, QCheckBox, QPushButton, QListWidget, QGridLayout, QGroupBox, QLineEdit, QMessageBox, QFileDialog from PySide6.QtCore import Qt import pandas as pd import webbrowser # 웹 브라우저를 열기 위한 모듈 # 네이버 블로그 제목을 가져오는 함수 def get_naver_titles(keyword, number): ret_titles = [] url = f"https://search.naver.com/search.naver?ssc=tab.blog.all&sm=tab_jum&query={keyword}" try: response = requests.get(url) response.raise_for_status() # HTTPError가 발생하면 예외가 발생합니다. soup = BeautifulSoup(response.content, 'html.parser') # 블로그 제목을 가져옴 (HTML 구조에 따라 class 이름을 조정) titles = soup.find_all('a', class_='title_link') for i, title in enumerate(titles[:number]): ret_titles.append(f"{i+1}. {title.get_text()}") except Exception as e: ret_titles.append(f"오류 발생: {str(e)}") return ret_titles class TitleExtractionApp(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): # 메인 레이아웃 main_layout = QVBoxLayout() # 키워드 입력 필드 추가 keyword_layout = QHBoxLayout() keyword_label = QLabel("키워드:") self.keyword_input = QLineEdit() keyword_layout.addWidget(keyword_label) keyword_layout.addWidget(self.keyword_input) # 리스트 박스 그룹 list_group_box = QGroupBox() list_layout = QVBoxLayout() # 결과 리스트 self.result_list = QListWidget() list_layout.addWidget(self.result_list) list_group_box.setLayout(list_layout) # 오른쪽 위젯들 그룹 right_group_box = QGroupBox() right_layout = QVBoxLayout() # 스핀박스 spin_layout = QHBoxLayout() spin_label = QLabel("추출할 항목 수") self.spin_box = QSpinBox() self.spin_box.setValue(5) spin_layout.addWidget(spin_label) spin_layout.addWidget(self.spin_box) # 추출 시작 버튼 start_button = QPushButton("추출 시작") start_button.clicked.connect(self.extract_titles) # 엑셀 저장 버튼 save_button = QPushButton("엑셀 저장") save_button.clicked.connect(self.save_to_excel) # '이런 기능이 있으면 좋겠어요' 버튼 추가 suggestion_label = QLabel("추가기능 제안하기") feedback_button = QPushButton("[기능 제안하기]\n이런 기능이 있으면 좋겠어요 ") feedback_button.clicked.connect(self.open_feedback_url) # '최신 무료 프로그램 소식 받기' 버튼 추가 program_news_button = QPushButton("최신 무료 프로그램 소식 받기") program_news_button.clicked.connect(self.open_program_news_url) # 레이아웃에 위젯 추가 right_layout.addLayout(spin_layout) right_layout.addWidget(start_button) right_layout.addWidget(save_button) right_layout.addWidget(feedback_button) # 새로운 버튼 추가 right_layout.addWidget(program_news_button) # 새로운 버튼 추가 right_group_box.setLayout(right_layout) # 그리드 레이아웃으로 왼쪽과 오른쪽 배치 grid_layout = QGridLayout() grid_layout.addWidget(list_group_box, 0, 0) grid_layout.addWidget(right_group_box, 0, 1) main_layout.addLayout(keyword_layout) main_layout.addLayout(grid_layout) self.setLayout(main_layout) self.setWindowTitle("제목 추출 프로그램") self.setGeometry(300, 300, 600, 400) self.setStyleSheet(""" QWidget { font-family: Arial, sans-serif; background-color: #f4f4f4; color: #333; } QLabel { font-size: 14px; font-weight: bold; } QGroupBox { border: 1px solid #ccc; border-radius: 8px; padding: 10px; margin-top: 10px; background-color: #ffffff; } QLineEdit { padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; } QSpinBox { padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; } QPushButton { padding: 10px 15px; font-size: 14px; background-color: #FFC107; color: white; border: none; border-radius: 4px; } QPushButton:hover { background-color: #E0A800; } QListWidget { padding: 10px; border: 1px solid #ccc; border-radius: 8px; background-color: #ffffff; } QHBoxLayout, QVBoxLayout { margin: 5px; } QGridLayout { padding: 10px; } """) def extract_titles(self): print("extract_titles start") keyword = self.keyword_input.text() number = self.spin_box.value() if not keyword: QMessageBox.warning(self, "입력 오류", "키워드를 입력해 주세요.") return try: titles = get_naver_titles(keyword, number) print(titles) if titles and "오류 발생" in titles[0]: raise Exception(titles[0]) self.result_list.clear() self.result_list.addItems(titles) except Exception as e: QMessageBox.critical(self, "오류 발생", str(e)) def save_to_excel(self): titles = [self.result_list.item(i).text() for i in range(self.result_list.count())] if not titles: QMessageBox.warning(self, "저장 오류", "저장할 내용이 없습니다.") return file_dialog = QFileDialog() file_path, _ = file_dialog.getSaveFileName(self, "엑셀 파일로 저장", "", "Excel Files (*.xlsx)") if file_path: df = pd.DataFrame({"Titles": titles}) df.to_excel(file_path, index=False) QMessageBox.information(self, "저장 완료", "엑셀 파일로 저장되었습니다.") def open_feedback_url(self): # 웹 브라우저에서 지정된 URL을 엽니다. webbrowser.open("https://www.naver.com") def open_program_news_url(self): # 웹 브라우저에서 지정된 URL을 엽니다. webbrowser.open("https://maily.so/binaryworker/embed?src=embed") if __name__ == '__main__': app = QApplication([]) window = TitleExtractionApp() window.show() app.exec()