[문서관리]Colab 노트북파일 html, pdf, 공유링크 생성 자동화

이 글의 목적

  • POC를 자주하다보면.. Colab에서 노트북 구동 후 그 결과를 소스코드 없이 간단하게 제공해야하는 순간이 온다.
  • 그 때마다 보려고 기록함
  • 얻을 수 있는 내용
    • notebook에 쓴 텍스트 목차 넣어서 html 만드는 것
    • notebook에서 그린 큰 그래프 따로 그림파일로 만들어 html에 넣는 것
    • pdf 변환
    • 구글드라이브 다운로드 공유 링크 출력

Pre-requisite

!pip install nbconvert
!pip install jupyter_contrib_nbextensions

자동화 스크립트

import base64
from bs4 import BeautifulSoup
import os

# === 사용자 설정 ===
notebook_input = "/content/drive/MyDrive/hdc_csp/nsquare_keyword.ipynb"
html_output_path = "/content/drive/MyDrive/hdc_csp/nsquare_keyword_final.html"
pdf_output_path = "/content/drive/MyDrive/hdc_csp/nsquare_keyword_final.pdf"
image_file_path = "/content/drive/MyDrive/hdc_csp/voc_wordcloud.png" # 꼭 html과 같은 경로에 있어야한다. 
temp_html_path = "/content/drive/MyDrive/hdc_csp/temp.html"

# === 1단계: nbconvert (출력 없이, Google Drive 폴더에 저장) ===
!jupyter nbconvert --to html --no-input "{notebook_input}" --output "{temp_html_path[:-5]}" > /dev/null 2>&1

# === 2단계: temp.html 열기 ===
with open(temp_html_path, "r", encoding="utf-8") as file:
    soup = BeautifulSoup(file, "html.parser")

# === 3단계: 'Output hidden' 텍스트 삭제 ===
for output_hidden in soup.find_all(string=lambda text: isinstance(text, str) and "Output hidden;" in text):
    output_hidden.extract()

# === 4단계: TOC 생성 (h1 > h2 > h3 트리 구조 완성) ===
toc_container = soup.new_tag("div", id="toc")
toc_title_tag = soup.new_tag("strong")
toc_title_tag.string = "📖 목차"
toc_container.append(toc_title_tag)

toc_list = soup.new_tag("ul")
toc_container.append(toc_list)

header_tags = soup.find_all(["h1", "h2", "h3"])

current_h1 = None
current_h2 = None

for idx, header in enumerate(header_tags):
    if not header.has_attr("id"):
        header['id'] = f"toc_{idx}"

    link = soup.new_tag("a", href=f"#{header['id']}")
    link.string = header.get_text()

    list_item = soup.new_tag("li")
    list_item.append(link)

    if header.name == "h1":
        toc_list.append(list_item)
        current_h1 = list_item
        current_h2 = None
    elif header.name == "h2":
        if current_h1 is None:
            toc_list.append(list_item)
        else:
            if not current_h1.find('ul'):
                current_h1.append(soup.new_tag("ul"))
            current_h1.find('ul').append(list_item)
        current_h2 = list_item
    elif header.name == "h3":
        if current_h2 is not None:
            if not current_h2.find('ul'):
                current_h2.append(soup.new_tag("ul"))
            current_h2.find('ul').append(list_item)
        elif current_h1 is not None:
            if not current_h1.find('ul'):
                current_h1.append(soup.new_tag("ul"))
            current_h1.find('ul').append(list_item)
        else:
            toc_list.append(list_item)

style_tag = soup.new_tag("style")
style_tag.string = """
#toc {
    position: fixed;
    top: 20px;
    right: 20px;
    width: 250px;
    background: #f9f9f9;
    border: 1px solid #ddd;
    padding: 10px;
    max-height: 90vh;
    overflow-y: auto;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    z-index: 1000;
    font-family: sans-serif;
    font-size: 14px;
}
#toc ul {
    list-style: none;
    padding-left: 0;
}
#toc li {
    margin: 5px 0;
}
#toc li ul {
    padding-left: 15px;
}
#toc li ul li ul {
    padding-left: 15px;
}
#toc a {
    text-decoration: none;
    color: #333;
}
#toc a:hover {
    text-decoration: underline;
}
"""
soup.head.append(style_tag)

if soup.body:
    soup.body.insert(0, toc_container)

# === 5단계: 이미지 base64 삽입 (문서 맨 마지막) ===
with open(image_file_path, "rb") as image_file:
    image_base64 = base64.b64encode(image_file.read()).decode('utf-8')

img_tag = soup.new_tag("img", alt="VOC 워드클라우드",
                       style="max-width: 100%; height: auto; display: block; margin: 20px auto;",
                       src=f"data:image/png;base64,{image_base64}")

if soup.body:
    soup.body.append(img_tag)

# === 6단계: 최종 HTML 저장 ===
with open(html_output_path, "w", encoding="utf-8") as file:
    file.write(str(soup))

print(f"최종 HTML 파일이 저장되었습니다: {html_output_path}")

# === 7단계: 중간 파일 삭제 ===
os.remove(temp_html_path)

# === 8단계: PDF 변환 ===
!apt-get -q install -y wkhtmltopdf > /dev/null
!wkhtmltopdf "{html_output_path}" "{pdf_output_path}" > /dev/null 2>&1

print(f"PDF 파일도 저장되었습니다: {pdf_output_path}")

# === 9단계: 다운로드 링크 생성 ===
from google.colab import files

print("다운로드 링크 (HTML):")
files.download(html_output_path)

print("다운로드 링크 (PDF):")
files.download(pdf_output_path)

# === 10단계: Google Drive 공유 링크 출력 ===
drive_file_id = html_output_path.split('/')[-1]
print(f"Google Drive 공유 링크 (HTML): https://drive.google.com/file/d/{drive_file_id}/view?usp=sharing")

pdf_file_id = pdf_output_path.split('/')[-1]
print(f"Google Drive 공유 링크 (PDF): https://drive.google.com/file/d/{pdf_file_id}/view?usp=sharing")