USER
import re
import tkinter as tk
from tkinter import filedialog, messagebox
import PyPDF2
import matplotlib.pyplot as plt
from wordcloud import WordCloud
import threading
import os
import sys
from konlpy.tag import Okt
from collections import Counter
import platform
import pyhwp
class WordCloudGenerator:
def __init__(self):
self.root = tk.Tk()
self.root.title("WordCloud Generator")
try:
self.okt = Okt()
except Exception as e:
messagebox.showerror("초기화 오류", f"형태소 분석기 초기화 실패: {str(e)}\nKoNLPy가 설치되어 있는지 확인해주세요.")
self.root.quit()
sys.exit(1)
self.setup_stopwords()
self.setup_font()
self.setup_gui()
def setup_font(self):
system = platform.system()
if system == 'Windows':
self.font_path = 'C:/Windows/Fonts/malgun.ttf'
if not os.path.exists(self.font_path):
self.font_path = 'C:/Windows/Fonts/gulim.ttc'
elif system == 'Darwin':
self.font_path = '/System/Library/Fonts/AppleSDGothicNeo.ttc'
else:
self.font_path = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf'
if not os.path.exists(self.font_path):
messagebox.showwarning("폰트 경고",
"기본 한글 폰트를 찾을 수 없습니다.\n"
"워드클라우드 생성 시 한글이 제대로 표시되지 않을 수 있습니다.")
self.font_path = None
def setup_stopwords(self):
self.korean_stop_words = {
'있', '하', '것', '들', '그', '되', '수', '이', '보', '않', '없', '나',
'사람', '주', '아니', '등', '같', '우리', '때', '년', '가', '한', '지',
'대하', '오', '말', '일', '그렇', '위하', '때문', '그것', '두', '말하',
'알', '그러나', '받', '못하', '일', '그런', '또', '문제', '더', '사회',
'많', '그리고', '좋', '크', '따르', '중', '나오', '가지', '씨', '시키',
'만들', '지금', '생각하', '그러', '속', '하나', '집', '살', '모르',
'적', '월', '데', '자신', '안', '어떤', '내', '경우', '명', '생각',
'시간', '그녀', '다시', '이런', '앞', '보이', '번', '나', '다른',
'어떻', '여자', '개', '전', '들', '사실', '이렇', '점', '싶', '말',
'정도', '좀', '원', '잘', '통하', '소리', '놓'
}
self.english_stop_words = {
'i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you',
"you're", "you've", "you'll", "you'd", 'your', 'yours', 'yourself',
'yourselves', 'he', 'him', 'his', 'himself', 'she', "she's", 'her',
'hers', 'herself', 'it', "it's", 'its', 'itself', 'they', 'them',
'their', 'theirs', 'themselves', 'what', 'which', 'who', 'whom',
'this', 'that', "that'll", 'these', 'those', 'am', 'is', 'are',
'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'having',
'do', 'does', 'did', 'doing', 'a', 'an', 'the', 'and', 'but', 'if',
'or', 'because', 'as', 'until', 'while', 'of', 'at', 'by', 'for',
'with', 'about', 'against', 'between', 'into', 'through', 'during',
'before', 'after', 'above', 'below', 'to', 'from', 'up', 'down',
'in', 'out', 'on', 'off', 'over', 'under', 'again', 'further',
'then', 'once'
}
def setup_gui(self):
"""GUI 요소를 설정합니다."""
self.frame = tk.Frame(self.root)
self.frame.pack(padx=60, pady=25)
self.status_label = tk.Label(self.frame, text="파일을 선택해주세요")
self.status_label.pack(pady=5)
self.length_frame = tk.Frame(self.frame)
self.length_frame.pack(pady=5)
tk.Label(self.length_frame, text="최소 글자 수:").pack(side=tk.LEFT)
self.min_length_var = tk.StringVar(value="2")
self.min_length_entry = tk.Entry(self.length_frame, textvariable=self.min_length_var, width=5)
self.min_length_entry.pack(side=tk.LEFT, padx=5)
self.max_words_frame = tk.Frame(self.frame)
self.max_words_frame.pack(pady=5)
tk.Label(self.max_words_frame, text="최대 단어 수:").pack(side=tk.LEFT)
self.max_words_var = tk.StringVar(value="50")
self.max_words_entry = tk.Entry(self.max_words_frame, textvariable=self.max_words_var, width=5)
self.max_words_entry.pack(side=tk.LEFT, padx=5)
self.load_button = tk.Button(
self.frame,
text="PDF, 텍스트, HWP파일 불러오기",
command=self.process_file_async
)
self.load_button.pack(pady=10)
def extract_text_from_pdf(self, pdf_path):
text = ""
try:
with open(pdf_path, 'rb') as file:
reader = PyPDF2.PdfReader(file)
for page in reader.pages:
text += page.extract_text() or "" # None 방지
except Exception as e:
raise Exception(f"PDF 파일 읽기 오류: {str(e)}")
return text
def extract_text_from_txt(self, txt_path):
encodings = ['utf-8', 'cp949', 'euc-kr']
for encoding in encodings:
try:
with open(txt_path, 'r', encoding=encoding) as file:
return file.read()
except UnicodeDecodeError:
continue
raise Exception("텍스트 파일의 인코딩을 확인할 수 없습니다.")
def extract_text_from_hwp(self, hwp_path):
"""HWP 파일에서 텍스트를 추출합니다."""
try:
# pyhwp를 사용하여 HWP 파일에서 텍스트를 추출합니다.
doc = pyhwp.HWPDocument(hwp_path)
text = ""
for section in doc.bodytext:
text += section.text.replace('\r\n', '\n') + '\n'
return text.strip()
except Exception as e:
raise Exception(f"HWP 파일 읽기 오류: {str(e)}")
def process_text(self, text):
try:
min_length = max(2, int(self.min_length_var.get()))
nouns = self.okt.nouns(text)
words = [word for word in nouns if len(word) >= min_length and word not in self.korean_stop_words]
english_words = re.findall(r'\b[a-zA-Z]+\b', text)
words.extend([word.lower() for word in english_words if len(word) >= min_length and word.lower() not in self.english_stop_words])
word_freq = Counter(words)
return dict(word_freq)
except Exception as e:
raise Exception(f"텍스트 처리 중 오류 발생: {str(e)}")
def generate_wordcloud(self, word_freq):
max_words = 50
try:
max_words = max(1, int(self.max_words_var.get()))
if not word_freq:
raise Exception("처리할 텍스트가 충분하지 않습니다.")
wordcloud = WordCloud(
font_path=self.font_path,
width=1200,
height=800,
background_color='white',
max_words=max_words,
min_font_size=10,
max_font_size=150,
random_state=42
).generate_from_frequencies(word_freq)
self.root.after(0, self.show_wordcloud, wordcloud)
except Exception as e:
raise Exception(f"워드클라우드 생성 중 오류 발생: {str(e)}")
def show_wordcloud(self, wordcloud):
plt.figure(figsize=(15, 10))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()
def process_file_async(self):
"""파일 처리를 비동기적으로 실행합니다."""
file_path = filedialog.askopenfilename(
filetypes=[
("지원되는 파일", "*.pdf;*.txt;*.hwp"),
("PDF 파일", "*.pdf"),
("텍스트 파일", "*.txt"),
("HWP 파일", "*.hwp"),
]
)
if not file_path:
return
self.status_label.config(text="파일 처리 중...")
self.load_button.config(state=tk.DISABLED)
def process():
try:
if file_path.lower().endswith('.pdf'):
text = self.extract_text_from_pdf(file_path)
elif file_path.lower().endswith('.txt'):
text = self.extract_text_from_txt(file_path)
elif file_path.lower().endswith('.hwp'):
text = self.extract_text_from_hwp(file_path)
elif file_path.lower().endswith('.docx'):
text = self.extract_text_from_docx(file_path)
else:
raise Exception("지원하지 않는 파일 형식입니다.")
word_freq = self.process_text(text)
self.generate_wordcloud(word_freq)
self.status_label.config(text="처리 완료")
except Exception as e:
messagebox.showerror("오류", str(e))
self.status_label.config(text="오류 발생")
finally:
self.load_button.config(state=tk.NORMAL)
threading.Thread(target=process, daemon=True).start()
def run(self):
self.root.mainloop()
if __name__ == "__main__":
app = WordCloudGenerator()
app.run() 해당 코드에서 pyhwp는 설치되어 있지만 모듈을 불러오지 못하면서 에러가 발생하고 있습니다. C:\>pip install pyhwp
Collecting pyhwp
Using cached pyhwp-0.1b15-py3-none-any.whl
Requirement already satisfied: cryptography in c:\programdata\anaconda3\lib\site-packages (from pyhwp) (43.0.0)
Requirement already satisfied: lxml in c:\programdata\anaconda3\lib\site-packages (from pyhwp) (5.2.1)
Requirement already satisfied: olefile>=0.43 in c:\programdata\anaconda3\lib\site-packages (from pyhwp) (0.47)
Requirement already satisfied: cffi>=1.12 in c:\programdata\anaconda3\lib\site-packages (from cryptography->pyhwp) (1.17.1)
Requirement already satisfied: pycparser in c:\programdata\anaconda3\lib\site-packages (from cffi>=1.12->cryptography->pyhwp) (2.21)
Installing collected packages: pyhwp
Successfully installed pyhwp-0.1b15 로 재설치하였습니다. 하지만 여전히 문제가 발생하고 있으므로 해결방법을 찾아 주십시오