본문 바로가기

AI Development

markitdown 완전 가이드 — PDF, Word, PPT를 LLM이 읽는 형식으로 자동 변환

반응형

RAG 파이프라인을 만들다 보면 항상 이 벽에 부딪혀요.

"이 PDF 분석해줘"
→ LLM: ????

이유:
PDF는 렌더링용 포맷
텍스트, 이미지, 표가 뒤섞인 이진 파일
LLM이 직접 이해하기 어려움

그래서 보통 이렇게 함:

PDF → PyPDF2로 텍스트 추출 → 근데 표/이미지 날아감
Word → python-docx → 복잡한 파싱 코드 작성
PPT → 슬라이드 하나씩 수동 처리
Excel → pandas로 읽고 또 변환

→ 포맷마다 다른 라이브러리
→ 포맷마다 다른 코드
→ 복잡하고 유지보수 힘듦

Microsoft가 이 문제를 해결하는 도구를 오픈소스로 냈어요. markitdown이에요.

pip install 'markitdown[all]'
markitdown 문서.pdf

끝.

GitHub 별 5만개+. MIT 라이선스.


뭘 지원하나

문서:
PDF, DOCX, PPTX, XLSX, CSV

웹:
HTML, XML, JSON, RSS

미디어:
이미지 (JPG, PNG) → LLM으로 설명 추출
음성 파일 (MP3, WAV) → Whisper로 자동 전사
YouTube URL → 자막 자동 추출

압축:
ZIP → 내부 파일 일괄 변환

설치

# 전체 기능 포함 설치 (권장)
pip install 'markitdown[all]'

# 최소 설치 (기본 문서만)
pip install markitdown

# 이미지 설명 추출 추가 (LLM 필요)
pip install 'markitdown[all]' openai

기본 사용 — CLI

# PDF 변환
markitdown 문서.pdf

# Word 변환
markitdown 보고서.docx

# PPT 변환
markitdown 발표자료.pptx

# Excel 변환
markitdown 데이터.xlsx

# 파일로 저장
markitdown 문서.pdf > output.md

# 여러 파일
markitdown *.pdf > all_docs.md

# YouTube 자막 추출
markitdown "https://www.youtube.com/watch?v=xxxxx"

# URL 웹페이지 변환
markitdown "https://docs.python.org/3/tutorial/"

기본 사용 — Python

from markitdown import MarkItDown

md = MarkItDown()

# PDF
result = md.convert("report.pdf")
print(result.text_content)

# Word
result = md.convert("document.docx")
print(result.text_content)

# Excel
result = md.convert("data.xlsx")
print(result.text_content)

# URL
result = md.convert("https://example.com")
print(result.text_content)

# 결과는 항상 result.text_content로 접근
# 포맷마다 다른 API 없음 — 통일된 인터페이스

실전 활용 1 — RAG 전처리 파이프라인

회사 문서를 LLM 지식베이스로 만드는 가장 흔한 사례예요.

from markitdown import MarkItDown
from pathlib import Path
import anthropic
import json

md = MarkItDown()
client = anthropic.Anthropic()

def process_documents(doc_dir: str) -> list[dict]:
    """폴더 안 문서들을 전부 Markdown으로 변환"""

    docs = []
    supported = ['.pdf', '.docx', '.pptx', '.xlsx', '.html', '.csv']

    for path in Path(doc_dir).rglob('*'):
        if path.suffix.lower() not in supported:
            continue

        try:
            result = md.convert(str(path))
            docs.append({
                "filename": path.name,
                "path": str(path),
                "content": result.text_content,
                "format": path.suffix
            })
            print(f"✅ {path.name}")
        except Exception as e:
            print(f"❌ {path.name}: {e}")

    return docs

def ask_about_docs(docs: list[dict], question: str) -> str:
    """변환된 문서들로 질문 답변"""

    # 문서 내용 합치기
    context = "\n\n---\n\n".join([
        f"[{doc['filename']}]\n{doc['content']}"
        for doc in docs
    ])

    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=2048,
        system="주어진 문서를 바탕으로 질문에 답해줘. 문서에 없는 내용은 모른다고 해.",
        messages=[{
            "role": "user",
            "content": f"문서 내용:\n{context}\n\n질문: {question}"
        }]
    )

    return response.content[0].text

# 실행
docs = process_documents("./company_docs")
print(f"\n{len(docs)}개 문서 처리 완료\n")

answer = ask_about_docs(docs, "우리 회사 환불 정책이 어떻게 되나요?")
print(answer)

실전 활용 2 — 이미지 설명 자동 추출

이미지가 포함된 PDF의 경우 LLM으로 이미지 내용까지 추출해요.

from markitdown import MarkItDown
from openai import OpenAI

# LLM 클라이언트 연결
openai_client = OpenAI()

md = MarkItDown(
    llm_client=openai_client,
    llm_model="gpt-4o"  # 이미지 이해 가능한 멀티모달 모델
)

# 이미지가 포함된 PDF 변환
# → 이미지 부분은 GPT-4o가 설명 텍스트로 변환
result = md.convert("technical_manual_with_diagrams.pdf")
print(result.text_content)

# 출력 예시:
# # 기술 매뉴얼
#
# ## 1장. 시스템 구성
#
# [이미지 설명: 3개의 서버가 로드밸런서에 연결된 클러스터 아키텍처 다이어그램.
# 왼쪽에 클라이언트, 중앙에 로드밸런서(Nginx), 오른쪽에 App Server 3대]
#
# 시스템은 다음과 같이 구성됩니다...

실전 활용 3 — Excel 데이터 분석 자동화

from markitdown import MarkItDown
import anthropic

md = MarkItDown()
client = anthropic.Anthropic()

def analyze_excel(filepath: str, question: str) -> str:
    """Excel 파일을 분석해서 질문에 답변"""

    # Excel → Markdown 표 변환
    result = md.convert(filepath)

    # 변환 결과 확인
    # | 월 | 매출 | 비용 | 순이익 |
    # |---|---|---|---|
    # | 1월 | 1,200만 | 800만 | 400만 |
    # | 2월 | 1,500만 | 850만 | 650만 |
    # ...

    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        messages=[{
            "role": "user",
            "content": f"""
다음 Excel 데이터를 분석해줘:

{result.text_content}

질문: {question}
"""
        }]
    )

    return response.content[0].text

# 사용
analysis = analyze_excel(
    "2026_Q1_sales.xlsx",
    "1분기에서 가장 성과가 좋은 제품 카테고리는?"
)
print(analysis)

실전 활용 4 — 회의록/음성 자동 처리

from markitdown import MarkItDown

md = MarkItDown()

# 회의 녹음 파일 → 텍스트 전사
result = md.convert("meeting_recording.mp3")
print(result.text_content)

# 출력:
# [00:00] 안녕하세요, 오늘 Q1 리뷰 회의를 시작하겠습니다.
# [00:15] 먼저 매출 현황부터 보면...
# ...

# YouTube 영상 → 자막 추출
result = md.convert("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
print(result.text_content)

실전 활용 5 — 일괄 변환 스크립트

from markitdown import MarkItDown
from pathlib import Path
import concurrent.futures
import time

md = MarkItDown()

def convert_file(path: Path) -> dict:
    """단일 파일 변환"""
    start = time.time()
    try:
        result = md.convert(str(path))
        duration = time.time() - start
        return {
            "status": "success",
            "file": path.name,
            "chars": len(result.text_content),
            "duration": f"{duration:.1f}초",
            "content": result.text_content
        }
    except Exception as e:
        return {
            "status": "error",
            "file": path.name,
            "error": str(e)
        }

def batch_convert(input_dir: str, output_dir: str):
    """폴더 전체 병렬 변환"""

    input_path = Path(input_dir)
    output_path = Path(output_dir)
    output_path.mkdir(exist_ok=True)

    supported = {'.pdf', '.docx', '.pptx', '.xlsx', '.html', '.csv'}
    files = [f for f in input_path.rglob('*') if f.suffix.lower() in supported]

    print(f"총 {len(files)}개 파일 변환 시작...")

    # 병렬 처리 (최대 4개 동시)
    with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
        results = list(executor.map(convert_file, files))

    # 결과 저장 + 통계
    success = 0
    fail = 0

    for result in results:
        if result["status"] == "success":
            # 변환된 내용을 .md 파일로 저장
            out_file = output_path / (Path(result["file"]).stem + ".md")
            out_file.write_text(result["content"], encoding="utf-8")
            print(f"✅ {result['file']} → {result['chars']:,}자 ({result['duration']})")
            success += 1
        else:
            print(f"❌ {result['file']}: {result['error']}")
            fail += 1

    print(f"\n완료: 성공 {success}개 / 실패 {fail}개")

# 실행
batch_convert("./documents", "./markdown_output")

MCP 서버로 Claude Desktop에서 직접 사용

# markitdown MCP 서버 설치
pip install markitdown-mcp

Claude Desktop 설정 (claude_desktop_config.json):

{
  "mcpServers": {
    "markitdown": {
      "command": "python",
      "args": ["-m", "markitdown_mcp"]
    }
  }
}

설정 후 Claude Desktop에서:

"이 PDF 분석해줘" → 파일 드래그 앤 드롭
→ markitdown이 자동으로 변환
→ Claude가 내용 분석

한계 알고 쓰기

잘 되는 것:
✅ 텍스트 중심 PDF
✅ Word/PPT/Excel 표
✅ HTML, CSV
✅ 음성 파일 전사
✅ YouTube 자막

잘 안 되는 것:
❌ 스캔된 PDF (OCR 별도 필요 → markitdown-ocr 플러그인)
❌ 복잡한 레이아웃 PDF (표 구조 깨질 수 있음)
❌ 이미지 내 텍스트 (LLM 연결 없으면 플레이스홀더)
❌ 암호화된 파일

복잡한 PDF가 필요하면:
→ Marker, MinerU 같은 전문 도구 고려
→ markitdown-ocr 플러그인 추가

요약 — 이런 상황에서 바로 써라

RAG 파이프라인 전처리:
→ 다양한 포맷 문서를 벡터 DB에 넣기 전
→ markitdown으로 일괄 변환 후 chunking

LLM에 문서 분석 요청:
→ PDF/Excel을 Claude/GPT에게 바로 줄 때
→ markitdown 변환 후 API 호출

사내 지식베이스:
→ 기존 문서들을 검색 가능한 형태로
→ 포맷 관계없이 한 번에 처리

회의록 자동 정리:
→ 녹음 파일 → 텍스트 → LLM 요약

 

반응형