나는 노션에 익숙한데, 자료정리를 무조건 pptx로 저장하라는 회사 이해x임. 여러번 노션이나 pdf를 권유했지만 팀장님의 마음을 돌릴 순 없었다 ㅠ gpt로 계속 만져보다가 실패를 거듭한 후 찾은 최선의 방법을 공유한다.
# 입력 HTML 파일 경로
title = 'cuda upgrade'
from bs4 import BeautifulSoup
from bs4.element import NavigableString
def parse_html_to_text(node, depth=0, ol_counters=None, current_heading_level=0):
if ol_counters is None:
ol_counters = {}
result = ''
# 현재 노드가 h1, h2, h3인지 확인하여 들여쓰기 수준 결정
if node.name in ['h1', 'h2', 'h3']:
indent_level = 0 # 헤딩은 들여쓰기 없음
else:
indent_level = current_heading_level + 1 # 추가 들여쓰기 적용
indent = '\t' * indent_level
# 헤딩 처리
if node.name in ['h1', 'h2', 'h3']:
# 헤딩 텍스트 출력
text = node.get_text(strip=True)
if text:
result += f"{text}\n" # 헤딩은 들여쓰기 없음
# 현재 헤딩 레벨 갱신
current_heading_level = int(node.name[1])
# 문단 처리
elif node.name == 'p':
text = node.get_text(strip=True)
if text:
result += f"{indent}{text}\n"
# 순서 있는 목록 처리
elif node.name == 'ol':
# 현재 깊이의 번호를 초기화하거나 start 속성 사용
start = node.get('start')
if start:
counter = int(start)
else:
counter = 1
ol_counters[depth] = counter
for li in node.find_all('li', recursive=False):
result += parse_html_to_text(li, depth, ol_counters, current_heading_level)
# 목록 종료 시 카운터 삭제
del ol_counters[depth]
# 순서 없는 목록 처리
elif node.name == 'ul':
for li in node.find_all('li', recursive=False):
result += parse_html_to_text(li, depth, ol_counters, current_heading_level)
# 목록 아이템 처리
elif node.name == 'li':
# 목록 아이템의 들여쓰기는 헤딩 레벨과 깊이에 따라 결정
item_indent_level = indent_level + depth
item_indent = '\t' * item_indent_level
if node.parent.name == 'ol':
number = ol_counters.get(depth, 1)
ol_counters[depth] = number + 1
bullet = f"{number}. "
else:
bullet = "- "
# 아이템의 텍스트 내용 추출
item_text = ''
for child in node.contents:
if isinstance(child, NavigableString):
item_text += child.strip()
elif child.name not in ['ol', 'ul']:
item_text += child.get_text(strip=True)
result += f"{item_indent}{bullet}{item_text}\n"
# 자식 요소 재귀 처리
for child in node.contents:
if child.name in ['ul', 'ol']:
result += parse_html_to_text(child, depth + 1, ol_counters, current_heading_level)
# 코드 블록 처리
elif node.name == 'pre':
code_text = node.get_text()
lines = code_text.split('\n')
for line in lines:
if line.strip(): # 빈 줄은 무시
result += f"{indent}{line}\n"
else:
result += "\n"
# 불필요한 요소 필터링
elif node.name in ['figure', 'img', 'style', 'script']:
pass # 해당 요소는 무시
# 텍스트 노드 처리
elif isinstance(node, NavigableString):
text = node.strip()
if text:
result += f"{indent}{text}\n"
# 기타 요소 처리
else:
for child in node.children:
result += parse_html_to_text(child, depth, ol_counters, current_heading_level)
return result
# 입력 HTML 파일 경로
input_file = title + '.html'
# 출력 텍스트 파일 경로
output_file = title + '.txt'
# HTML 파일 읽기
with open(input_file, 'r', encoding='utf-8') as f:
html_content = f.read()
# BeautifulSoup으로 파싱
soup = BeautifulSoup(html_content, 'html.parser')
# body 태그 선택
body = soup.find('body')
# 텍스트 변환 실행
text_output = parse_html_to_text(body)
# 결과를 텍스트 파일로 저장
with open(output_file, 'w', encoding='utf-8') as f:
f.write(text_output)
print(f"변환이 완료되었습니다. 결과는 '{output_file}'에 저장되었습니다.")
from pptx import Presentation
from pptx.util import Inches, Pt
import re
# 입력 텍스트 파일 경로
input_txt_file = output_file
# 출력 PPTX 파일 경로
output_pptx_file = title + '.pptx'
# 프레젠테이션 객체 생성
prs = Presentation()
# 슬라이드 크기를 와이드스크린(16:9)으로 설정
prs.slide_width = Inches(13.33)
prs.slide_height = Inches(7.5)
# 텍스트 파일 읽기
with open(input_txt_file, 'r', encoding='utf-8') as f:
lines = f.readlines()
# 1. 첫 장에 제목 슬라이드 추가 (slide_layouts[0] 사용)
title_slide_layout = prs.slide_layouts[0] # 제목 슬라이드 레이아웃
title_slide = prs.slides.add_slide(title_slide_layout)
# 첫 번째 줄을 제목으로 사용
first_line = lines[0].strip() # 첫 번째 줄에서 개행 문자 제거
# 제목과 부제목 설정
title = title_slide.shapes.title
title.text = first_line # 첫 번째 줄을 제목으로 설정
# 이후 슬라이드 레이아웃 선택 (제목 및 내용)
slide_layout = prs.slide_layouts[1]
current_slide = None
bullet_levels = []
for i, line in enumerate(lines):
stripped_line = line.strip('\n')
# 들여쓰기 수준 계산
indent_level = len(re.match(r'^(\t*)', stripped_line).group(1))
content = stripped_line.lstrip('\t')
if i == 0:
# 첫 번째 슬라이드는 이미 제목으로 사용했으므로 건너뜀
continue
# 헤딩인지 확인 (h1, h2, h3는 들여쓰기 없음)
if indent_level == 0:
# 새로운 슬라이드 생성
current_slide = prs.slides.add_slide(slide_layout)
title_placeholder = current_slide.shapes.title
body_placeholder = current_slide.placeholders[1]
tf = body_placeholder.text_frame
tf.clear()
# 두 번째 슬라이드의 제목도 첫 번째 줄을 사용
title_placeholder.text = first_line if i == 1 else content
bullet_levels = []
else:
if current_slide is None:
# 슬라이드가 없으면 새로 생성
current_slide = prs.slides.add_slide(slide_layout)
body_placeholder = current_slide.placeholders[1]
tf = body_placeholder.text_frame
tf.clear()
else:
body_placeholder = current_slide.placeholders[1]
tf = body_placeholder.text_frame
# 리스트 아이템인지 확인
bullet_match = re.match(r'^(- |\d+\. )(.*)', content)
if bullet_match:
bullet_text = bullet_match.group(2)
p = tf.add_paragraph()
p.text = bullet_text
p.level = indent_level - 1 # 들여쓰기 수준에 따라 bullet level 설정
else:
# 일반 텍스트
p = tf.add_paragraph()
p.text = content
p.level = indent_level - 1 # 들여쓰기 수준에 따라 bullet level 설정
# 프레젠테이션 저장
prs.save(output_pptx_file)
print(f"텍스트 파일이 '{output_pptx_file}'로 변환되었습니다.")
코드 더 수정해야지... 아직 태그에 종속적인 코드다.