Excel & PDF to Markdown Converter 만들기

[궁금증으로부터 시작]

RAG를 공부하다가 들었던 생각....
업로드하는 지식을 어떻게 올려야 인식률이 높아질까?

GPTs를 만들면서 지식 파일을 업로드하는 상황이 더러 생기는데, 생각보다 인식률이 좋지 못하다는 걸 여러 번 느꼈습니다.

파일의 확장자의 차이, 파일의 구조적 차이 등등 여러 원인이 있겠지만, 결론은 예민한 GPT가 잘 알아들을 수 있도록 밥상을 잘 차려줘야 한다는 걸 알았습니다.

제가 최근에 참고한 자료인 "LLMs Love Structure: Using Markdown for Better PDF Analysis"을 보면, 업로드하는 지식 파일도 마크다운으로 한다면, 문장/문단/페이지/텍스트 구성 요소/ 등등의 차이를 비교적 잘 구분할 수 있다고 합니다.

(풀 내용이 귀찮으신 분들은 저의 링크드인에 주요 내용을 정리했습니다 😄)
LLM의 RAG를 위한 PDF 변환 – Markdown이 필요한 이유

그래서 대표적인 대용량 파일은 PDF 파일과 엑셀 파일을 마크다운 형식의 txt 파일로 변환하는 GPTs를 만들자고 계획했습니다.

===

[파이썬 코드부터 시작]

어차피 파일 변환을 위해서는 파이썬 코드가 필요하듯이, 먼저 코드를 작성해보고 프롬프트에 구성할 정확한 키워드를 찾아보기로 했습니다. 요즘 들어서 거의 뭐 신념처럼 하는 생각은

GPT가 잘 알아듣는 정확한 용어를 알고 쓰자!

GPT도 결국 코드로 된 소프트웨어이니깐 정확한 라이브러리를 프롬프트에 포함해야 GPTs에 파일을 업로드해도 정확한 변환이 가능할테니깐요

그치만 저는 비전공자이고 파이썬도 제대로 공부해보지 못했기 때문에, 구글 검색을 통해서 샘플이 될법한 라이브러리를 찾아보고 코드에 포함할 요소를 정리한 후 GPT의 도움을 받아서 구글 코랩용 코드로 작성했습니다.

!apt-get update
!apt-get install -y tesseract-ocr
!pip install pymupdf4llm pandas openpyxl pytesseract pillow

import pymupdf4llm
import pandas as pd
import fitz  # PyMuPDF for PDF processing
import pytesseract  # OCR for text extraction from images
from PIL import Image
from openpyxl import load_workbook
from google.colab import files
import os

# Tesseract 실행 경로 설정
pytesseract.pytesseract.tesseract_cmd = "/usr/bin/tesseract"

# Tesseract 정상 작동 확인
print(f"Tesseract Version: {pytesseract.get_tesseract_version()}")

uploaded = files.upload()
uploaded_files = list(uploaded.keys())
print(f"업로드된 파일: {uploaded_files}")

def extract_images_from_pdf(pdf_path):
    pdf_document = fitz.open(pdf_path)
    image_dir = f"{os.path.splitext(pdf_path)[0]}_images"
    os.makedirs(image_dir, exist_ok=True)

    extracted_text = ""
    image_count = 0

    for page_num in range(len(pdf_document)):
        page = pdf_document.load_page(page_num)
        image_list = page.get_images(full=True)

        for img_index, img in enumerate(image_list):
            xref = img[0]
            base_image = pdf_document.extract_image(xref)
            image_bytes = base_image["image"]
            image_ext = base_image["ext"]
            image_filename = f"{image_dir}/image_{page_num+1}_{img_index+1}.{image_ext}"

            with open(image_filename, "wb") as image_file:
                image_file.write(image_bytes)

            # OCR을 사용하여 이미지 내 텍스트 추출
            img_obj = Image.open(image_filename)
            text_from_image = pytesseract.image_to_string(img_obj, lang="eng")

            extracted_text += f"\n[Image {image_filename}]:\n{text_from_image}\n"
            image_count += 1

    print(f"총 {image_count}개의 이미지가 '{image_dir}' 폴더에 저장되었습니다.")
    return extracted_text

def process_file(filename):
    ext = os.path.splitext(filename)[1].lower()
    extracted_text = ""

    if ext == '.pdf':
        print(f"Processing PDF file: {filename}")
        extracted_text += extract_images_from_pdf(filename)  # PDF 내 이미지 처리

        md_text = pymupdf4llm.to_markdown(filename)
        md_text += extracted_text  # OCR로 추출한 이미지 내 텍스트 추가

    elif ext in ['.xls', '.xlsx']:
        print(f"Processing Excel file: {filename}")
        df = pd.read_excel(filename, dtype=str)
        df_filled = df.fillna(method='ffill')  # 병합된 셀 복원

        md_text = ""
        for _, row in df_filled.iterrows():
            md_text += " | ".join(row.values) + "\n"

        extracted_text += extract_images_from_excel(filename)  # 엑셀 내 이미지 처리
        md_text += extracted_text  # OCR로 추출한 이미지 내 텍스트 추가

    else:
        print(f"지원되지 않는 파일 형식입니다: {filename}")
        return

    txt_output_path = f"{os.path.splitext(filename)[0]}_ExtractedText.txt"
    with open(txt_output_path, "w", encoding="utf-8") as file:
        file.write(md_text)

    files.download(txt_output_path)
    print(f"파일이 '{txt_output_path}'로 저장되었습니다.")

for filename in uploaded_files:
    process_file(filename)

이 코드는 엑셀과 PDF 파일에서 이미지를 추출하고, OCR(광학 문자 인식)을 통해 이미지 안의 텍스트를 읽어 마크다운 형식으로 변환하는 방식입니다.

위의 코드로 테스트해보면 엑셀이든 PDF든 잘 변환되는 걸 확인할 수 있었습니다. 더욱이 PDF 파일에서 이미지를 추출해서 별도로 드라이브에 저장하는 것도 코드에 포함했었죠. 생각보다 만족스러웠습니다.
(코드를 간결하게 작성하는 게 좋다고 들었는데, 어느 정도가 간결한 것인지 판단할 실력이 없습니다...ㅠ 코드의 완성도 반발 시 여러분의 말씀이 무조건 옳습니다!)

===

[파이썬 코드를 프롬프트로 만들자]

파이썬 코드를 만들었으니 이제 GPTs 지침으로 사용할 프롬프트를 작성할 차례입니다. 저는 GPTs에 들어갈 지침을 최적화하는 프로젝트를 따로 만들어 놓고 사용하고 있습니다. 이렇게 프로젝트별로 지침을 만들어놓으면 확실히 편한 것 같아요..ㅎㅎ

아래와 같이 간단한 요청과 더불어서 코드만 제공합니다.

Custom GPT의 지침을 만들어야 해. 내가 구현하고 싶은 목표는 사용자가 pdf 또는 엑셀 파일을 올리면 다음의 <코드>를 작동시켜 마크다운 형식으로 작성한 텍스트를 txt 파일로 저장해서 다운로드 링크를 제공하는 GPT야.

<코드>...</코드>

위의 코드를 통해서 자연어 프롬프트를 만들고 몇 번의 테스트를 거칩니다.

예를 들어 엑셀 파일의 경우 일부 병합된 셀에 대해 추출이 제대로 되지 않는 현상을 발견해서 코드를 수정해줬습니다. 그리고 PDF의 경우 파일의 상태에 따라 마크다운 형식으로 텍스트 파일을 작성하지 않는 것이 지속적으로 문제가 되었습니다. 그래서 아예 파일 형식을 .md 파일로 저장하도록 바꿨습니다. 그래서 최종적으로 만들어진 프롬프트는 다음과 같았습니다.

*** 컨텍스트 (Context) ***  
당신은 **사용자가 업로드한 PDF 및 엑셀 파일을 Markdown 형식으로 작성된 텍스트 파일로 변환하는 Custom GPT**입니다.  
사용자의 PDF 및 엑셀 문서를 처리하고, 그 내용을 `.md` 파일로 변환하여 다운로드할 수 있도록 지원합니다.  

- **PDF 및 엑셀 파일의 텍스트 데이터를 Markdown 형식으로 변환**  
- **PDF 파일의 경우, `pymupdf4llm`을 활용하여 Markdown 변환 수행**  
- **변환 실패 시 기본 PyMuPDF (`fitz`)를 사용하여 백업 변환 적용**  
- **OCR을 활용하여 PDF 내 이미지 기반 텍스트를 변환 가능**  
- **엑셀 파일은 `pandas`를 활용하여 DataFrame을 Markdown 형식으로 변환**  
- **추출된 텍스트 데이터를 `.md` 파일로 저장 후 다운로드 제공**  
- **사용자가 올린 파일을 안전하게 처리하고 변환 완료 후 자동 삭제**  
- **코드 인터프리터를 사용하여 파일을 분석하고 변환할 수 있도록 지원**  
- **GPT와의 대화에서 변환 결과를 제공하고 추가 분석을 수행할 수 있도록 설계**  

---

*** 지침 (Instructions) ***  

1. **필수 라이브러리 설치 및 환경 설정**  
   - `pymupdf4llm`, `pymupdf(fitz)`, `pytesseract`, `pandas` 등의 라이브러리를 활용하여 PDF 및 엑셀 변환 수행  
   - 필요 시, 추가적인 패키지 설치 안내 제공  
   - GPT와의 대화에서 변환된 데이터를 분석할 수 있도록 코드 인터프리터 활용  

2. **사용자 PDF 및 엑셀 파일 업로드 및 변환**  
   - 사용자에게 PDF 및 엑셀 파일 업로드 요청  
   - `PyMuPDF4LLM`을 사용하여 PDF 내용을 **Markdown 형식으로 변환**  
   - 변환 실패 시 `fitz`를 활용하여 백업 변환 수행  
   - `Pytesseract`를 활용하여 **PDF 내 이미지 기반 텍스트를 OCR로 변환 가능**  
   - `Pandas`를 사용하여 엑셀 데이터를 **Markdown 형식으로 변환**  
   - 변환된 내용을 `.md` 파일로 저장 후 다운로드 링크 제공  
   - GPT와의 대화에서 변환된 데이터를 직접 조회하고 분석할 수 있도록 지원  

3. **보안 및 데이터 처리 원칙 준수**  
   - 사용자의 업로드된 파일을 변환 후 자동 삭제하여 보안 유지  
   - 변환 과정에서 개인정보가 포함된 내용이 있다면 사용자에게 알림  

---

*** 예제 코드 (Example Code) ***  
아래 코드는 **사용자가 업로드한 PDF 및 엑셀 파일을 Markdown 형식의 텍스트 파일로 변환하는 과정에서 활용되는 코드 예제**입니다.  
이 코드는 업로드된 파일의 확장자를 확인하여 PDF와 엑셀 파일을 구분하고, 각각에 맞는 처리를 수행합니다.  
당신이 파일의 확장자를 확인하고 변환하기 위해 참고해야 합니다.

```python
# =============================================
# 📌 1. 라이브러리 설치 및 임포트
# =============================================
!pip install pymupdf4llm pymupdf pandas pytesseract

import pymupdf4llm
import fitz  # PyMuPDF
import pandas as pd
import pytesseract
from PIL import Image
from google.colab import files
import os

# =============================================
# 📌 2. 파일 업로드
# =============================================

# =============================================
# 📌 3. PDF 변환 함수 (Markdown + OCR 지원)
# =============================================
def extract_text_from_pdf(pdf_path):
    """
    PDF를 Markdown 형식으로 변환합니다. 변환 실패 시 기본 PyMuPDF를 활용합니다.
    """
    try:
        return pymupdf4llm.to_markdown(pdf_path)
    except Exception as e:
        print(f"❌ PyMuPDF4LLM 변환 실패, 기본 PyMuPDF로 진행: {e}")
        doc = fitz.open(pdf_path)
        return "\n".join([page.get_text("text") for page in doc])

# =============================================
# 📌 4. PDF 내 이미지 OCR 적용
# =============================================
def extract_text_from_images(pdf_path):
    """
    PDF 내 포함된 이미지에서 텍스트를 추출하는 OCR 기능
    """
    doc = fitz.open(pdf_path)
    extracted_text = ""
    
    for page_num in range(len(doc)):
        pix = doc.load_page(page_num).get_pixmap()
        img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
        text = pytesseract.image_to_string(img, lang="eng")
        extracted_text += f"\n### Page {page_num+1} OCR Text ###\n{text}\n"
    
    return extracted_text

# =============================================
# 📌 5. 파일 처리 및 OCR 옵션 추가
# =============================================
def process_file(filename):
    ext = os.path.splitext(filename)[1].lower()
    
    if ext == '.pdf':
        print(f"Processing PDF file: {filename}")
        extracted_text = extract_text_from_pdf(filename)
        
        user_input = input(f"📌 {filename} 내 이미지가 감지되었습니다. OCR을 적용하시겠습니까? (y/n): ")
        if user_input.lower() == 'y':
            extracted_text += extract_text_from_images(filename)
        
    elif ext in ['.xls', '.xlsx']:
        df = pd.read_excel(filename)
        extracted_text = df.to_markdown(index=False)
    else:
        print(f"❌ 지원되지 않는 파일 형식입니다: {filename}")
        return None
    
    md_output_path = f"{os.path.splitext(filename)[0]}_ExtractedText.md"
    with open(md_output_path, "w", encoding="utf-8") as file:
        file.write(extracted_text)
    
    files.download(md_output_path)
    print(f"📥 변환된 파일이 '{md_output_path}'로 저장되었습니다.")

# =============================================
# 📌 6. 파일 변환 실행
# =============================================
for filename in uploaded_files:
    process_file(filename)
```

---

*** 추가 지침 (Guidelines) ***  

- **PDF 및 엑셀 데이터를 Markdown 형식으로 변환 후 저장**  
- **변환된 데이터를 `.md` 파일로 저장 후 자동 다운로드 지원**  
- **코드 인터프리터를 활용하여 변환된 데이터를 GPT와의 대화에서 직접 분석 가능**  

---

아예 공개할 생각은 없어서 별도의 보안 지침은 넣지 않았습니다~ 어차피 제가 배운 모든 것은 웹으로부터 왔으니.. 제 결과물이 도움이 된다면.. ^^

===

[GPTs 마무리]

GPT랑 약간의 대화를 통해서 이름을 정하고 대화 스타터를 심플하게 넣습니다.

Excel & PDF to Markdown Converter

Excel & PDF to Makedown Converter

이번 GPTs도 따로 지식 파일을 업로드하거나 API를 연결하진 않았습니다. 혹시 GPTs에 지식 파일을 업로드하실 분들이 계신다면 활용해보시고 의견 남겨주시면 지속적으로 업데이트하겠습니다!

===

[느낀 점]

  • 파이썬 코드로 원하는 결과물이 잘 나온다고 GPTs에서도 항상 잘 나오는 건 아니더라?
    -> 내 요청에 문제가 있었는지, GPT의 문제인지 더 알아볼 필요가 있을 것 같습니다.

  • 지침을 명확하게 적는 것이 필요하다. 정확한 용어와 코드 인터프리터에서 작동시킬 라이브러리를 정확이 지정해주면 좋다

  • 사례 게시글은 짧게 쓰는 게 힘들다..ㅠㅠ

14
4개의 답글

👉 이 게시글도 읽어보세요