혼자 울면서 한 프로젝트입니다...
1. Overview & IDEA - PPT Searching 2. Extracting with PPT 3. TF-IDF with PPT 4. Threading with PPT 5. Operating with PPT
PoC(Proof of Concept)
조아써! 일딴 시작을 해보자
Library 리서치
일딴 ppt를 열어서 문자열을 가져올 수 있어야 할 거아니냐? pptx라는 파일 포멧을 읽고 이걸 꺼꾸로 파싱해서 값을 가져와야하는 고민 보다 멘땅에 헤딩 하다보니 python Library에 python-pptx라는 라이브러리가 있다는 걸 알게되었다.
https://python-pptx.readthedocs.io/en/latest/
그렇다면 얘를 통해서 .pptx 파일을 읽을 수 있다는 것을 우선 확인했다.
사실 ppt에 제목과 글에 가중치를 줄 수 도 있었지만 이렇게 진행하지는 않았다.
단일 ppt 텍스트 추출
# dependency
# nltk 3.7
# konlpy 0.6.0
# wordcloud 1.8.2.2
# pptx 0.6.21
# pip install nltk
# pip install konlpy
# pip install wordcloud
# pip install python-pptx
# jre 설치 필요
# command code : 쳐야하는 코드
# python simple_keywording "[파이썬 파일과 같은 위치에 있는 대상 ppt이름]"
# load komoran
komoran = Komoran()
# stopword embedding
# stopwords ( text from : https://raw.githubusercontent.com/yoonkt200/FastCampusDataset/master/korean_stopwords.txt )
with open(stopwords_path, "r", encoding="utf-8") as f:
list_file = f.readlines()
stopwords = list(map(lambda x: x.replace("\n", ""), list_file))
# function : extract string from pptx
# input : string = ppt_name where file extension is .pptx
# output : string = low ppt text
# dependancy : pptx
def extract_pptx_cell_text(shape):
ret_cell_text = ""
row_count = len(shape.table.rows)
col_count = len(shape.table.columns)
for _r in range(0, row_count):
for _c in range(0, col_count):
ret_cell_text += shape.table.cell(_r, _c).text + " "
return ret_cell_text
def extract_pptx_low_text_str(ppt_name):
low_text_list = ""
input = Presentation(ppt_name)
for slide in input.slides:
for shape in slide.shapes:
if shape.shape_type == MSO_SHAPE_TYPE.TABLE:
low_text_list += extract_pptx_cell_text(shape)
if not shape.has_text_frame:
continue
for paragraph in shape.text_frame.paragraphs:
low_text_list += paragraph.text + " "
return low_text_list.rstrip()
# basic preprocessing
# input : String
# output : String
# dependancy : re
def preprocess(text):
text = text.strip()
text = re.compile("[%s]" % re.escape(string.punctuation)).sub(" ", text)
text = re.sub("\s+", " ", text)
text = re.sub(
r"\b\d+\b|[^\w\s]",
lambda match: match.group() if match.group().startswith("\b") else "",
text,
)
text = re.sub(r"[^\w\s]", " ", str(text).strip())
text = re.sub(r"\s+", " ", text)
return text
# function : extracting keyword from komoran
# input : String
# output : String
# dependancy : konlpy,
def final(text):
n = []
word = komoran.nouns(text)
p = komoran.pos(text)
# hannanum_word = Hannanum().nouns(text)
# print(hannanum_word)
# word +=hannanum_word
# hannanum_p = Hannanum().pos(text)
# print(hannanum_p)
for pos in p:
if pos[1] in ["SL"]:
word.append(pos[0])
# for hpos in hannanum_p:
# if hpos[1] in ["SL"]:
# word.append(hpos[0])
for w in word:
if len(w) > 1 and w not in stopwords:
n.append(w)
# print(word)
return " ".join(n)
# function : all preprocessing file
# input : string = pptx title
# output : string = keyword with " "
def final_preprocessing(ppt_name):
return final(preprocess(extract_pptx_low_text_str(ppt_name)))
그래도 인턴이라고 회사에 누가 되지 않기 위해 이번에 코드는 정말 잘 정리해놨다… 뻐덧 위에 주석도 붙여놨고!
final_preprocessing 함수에 ppt경로가 변수로 실행되면
final -> preprocess -> extract_pptx_low_text_str -> extract_pptx_cell_text
위와 같은 순서로 함수가 실행된다.
final함수는 코모란을 이용해서 불용어를 처리한다.(함수명이 좋지 않은 것 같다 다시보니)
preprocess는 텍스트에 존재하는 많은 불필요한 문자열들을 제거했다.
extract_pptx_low_text_str는 ppt path를 받아서 각 장을 순회 하면서 모든 cell에서 문자열을 뽑아내서 긴 문자열을 만들어내는데 이떄 1장에 슬라이드에서 문자열을 추출하는 부분을 extract_pptx_cell_text가 해준다.
폴더를 기준으로 하위폴더에 pptx를 순회
def folder2data(any_folder_path):
return_dic = {}
f_list = os.listdir(any_folder_path)
pattern = r"\.pptx$"
print(
" now extracting in {} folder wiht length {} loading...".format(
any_folder_path, len(f_list)
)
)
start = time.time()
for file_path in f_list:
absoulte_folder_path = os.path.join(any_folder_path, file_path)
if os.path.isdir(os.path.join(any_folder_path, file_path)):
child_path = os.path.join(any_folder_path, file_path)
return_dic.update(folder2data(child_path))
elif (
errorFileDetection(absoulte_folder_path)
and re.search(pattern, file_path)
and file_path.find("~$") == -1
and os.stat(os.path.join(any_folder_path, file_path)).st_size > 0
):
parent_path = os.path.join(any_folder_path, file_path)
return_dic.update(file2data(parent_path))
print(" end extracting in {} folder...".format(any_folder_path))
end = time.time()
print(" extracting folder time {} sec".format(end - start))
return return_dic
요 함수가 바로 폴더를 기준으로 하위 폴더와 pptx를 순회하면서 dictionary구조로 만들어주는 코드이다. 이때 뭔가 재귀로 진짜 프로그래밍을 해본거 같아서 기분이 굉장히 좋았던 기억이 있다.
여튼 이렇게 프로그램을 톨리면 .pptx로 끝나는 모든 ppt에 대해서
ppt_Name : string
구조로 데이터를 뽑아낼 수 있다.
->다음편3. TF-IDF with PPT에서 계속