Python Office 라이브러리 — 빠른 시작
office_oxide는 Office 문서 처리 분야에서 가장 빠른 Python 라이브러리입니다. 순수 Rust 코어, Python다운 API, 의존성 없음. DOCX, XLSX, PPTX(및 레거시 DOC, XLS, PPT)를 1밀리초 미만에 읽습니다 — python-docx, openpyxl, python-pptx보다 8~100배 빠릅니다.
설치
pip install office-oxide
CPython 3.8~3.14를 위한 wheel이 Linux, macOS, Windows용으로 배포됩니다. PyPI 패키지 이름은 office-oxide(하이픈), 임포트 이름은 office_oxide(언더스코어)입니다.
문서 읽기
from office_oxide import Document
with Document.open("report.docx") as doc:
print(doc.plain_text())
한 줄 호출 형태도 사용할 수 있습니다:
import office_oxide
print(office_oxide.extract_text("report.docx"))
핵심 API
Document.open은 파일 확장자에서 형식을 감지하고(매직 바이트로 다시 검증), str, bytes, os.PathLike를 받습니다. 컨텍스트 매니저로 사용하면 네이티브 메모리가 결정적으로 해제됩니다.
from pathlib import Path
from office_oxide import Document
with Document.open(Path("data/deck.pptx")) as doc:
print(doc.format) # "pptx"
print(doc.plain_text()) # str
print(doc.to_markdown()) # str — GitHub Flavored Markdown
print(doc.to_html()) # str — 시맨틱 HTML
ir = doc.to_ir() # 중첩 dict (DocumentIR)
doc.save_as("deck.docx") # 레거시 PPT → PPTX도 가능
파일이 디스크에 없을 때 원시 바이트에서 열기:
data = open("report.xlsx", "rb").read()
with Document.from_bytes(data, "xlsx") as doc:
print(doc.plain_text())
모듈 레벨 단축 함수:
import office_oxide
office_oxide.extract_text("file.docx") # → str
office_oxide.to_markdown("file.pptx") # → str
office_oxide.to_html("file.xlsx") # → str
office_oxide.version() # → "0.1.0"
편집
EditableDocument는 변경되지 않은 OPC 파트(이미지, 차트, 스타일, 관계)를 저장 시 그대로 보존합니다. DOCX, XLSX, PPTX만 지원합니다.
from office_oxide import EditableDocument
with EditableDocument.open("template.docx") as ed:
n = ed.replace_text("{{name}}", "Alice")
print(f"{n}건 치환")
ed.save("out.docx")
DOCX / PPTX의 텍스트 치환
from office_oxide import EditableDocument
with EditableDocument.open("slides.pptx") as ed:
ed.replace_text("Q3", "Q4")
ed.replace_text("2024", "2025")
ed.save("slides_q4.pptx")
replace_text는 DOCX에서는 <w:t> 요소를, PPTX에서는 모든 슬라이드의 <a:t>를 순회하며 치환 횟수를 반환합니다.
XLSX 셀 쓰기
from office_oxide import EditableDocument
with EditableDocument.open("budget.xlsx") as ed:
ed.set_cell(0, "A1", "Total") # 문자열
ed.set_cell(0, "B1", 42.5) # 숫자 (int도 가능)
ed.set_cell(0, "C1", True) # 불리언
ed.set_cell(0, "D1", None) # 비움
ed.save("budget.xlsx")
sheet_index는 0부터 시작; cell_ref는 표준 스프레드시트 표기법(A1, AA12)입니다.
형식 무관 IR
doc.to_ir()는 Rust의 DocumentIR을 그대로 반영하는 중첩 dict을 반환합니다 — 제목, 단락, 표, 목록, 이미지의 섹션들입니다. 파이프라인이나 LLM에 구조화된 컨텍스트를 주입할 때 유용합니다.
ir = doc.to_ir()
for section in ir["sections"]:
print(section.get("title"))
for el in section["elements"]:
kind = el["kind"] # "Heading" | "Paragraph" | "Table" | "List" | ...
바이트 기반 파이프라인
from_bytes는 서버리스/스트리밍 워크플로에서 임시 파일을 만들 필요가 없게 해줍니다:
import requests
from office_oxide import Document
data = requests.get("https://example.com/doc.docx").content
with Document.from_bytes(data, "docx") as doc:
print(doc.to_markdown())
레거시 형식
DOC, XLS, PPT도 동일한 API로 처리합니다. 확장자에서 레거시 CFB 파서가 자동 선택되고, save_as로 모던 OOXML로 투명하게 변환할 수 있습니다:
with Document.open("legacy.doc") as doc:
print(doc.plain_text())
doc.save_as("legacy.docx") # 한 줄로 DOC → DOCX
오류
파싱/IO 실패는 OfficeOxideError를 발생시킵니다. save_as의 IO 실패는 IOError로 래핑됩니다.
from office_oxide import Document, OfficeOxideError
try:
with Document.open("weird.file") as doc:
print(doc.plain_text())
except OfficeOxideError as e:
print(f"office_oxide 실패: {e}")
except FileNotFoundError:
print("파일이 없습니다")
문제 해결
| 증상 | 원인 |
|---|---|
OfficeOxideError: unsupported format: "" |
경로에 확장자가 없습니다 — Document.from_bytes(data, "docx")를 사용하세요. |
RuntimeError: Document is closed |
with 블록을 빠져나간 뒤에도 핸들을 잡고 있습니다. 새로 열어야 합니다. |
ImportError: _native |
wheel이 플랫폼과 맞지 않습니다. pip install --force-reinstall office-oxide. |
| 레거시 DOC가 깨져 보임 | 파일이 암호화되었을 수 있습니다(Word 97 RC4). office_oxide는 복호화하지 않으므로 LibreOffice로 먼저 복호화하세요. |
| Windows에서 Unicode 문제 | 바이트 경로 대신 pathlib.Path를 사용하세요. Document.open이 플랫폼 인코딩을 처리합니다. |
더 보기
- python-docx에서 마이그레이션 — 그대로 교체, 14배 빠름
- openpyxl에서 마이그레이션 — XLSX 커버리지 동일, 18배 빠름
- python-pptx에서 마이그레이션 — PPTX 46배 빠름
- 성능 벤치마크 — 6,062개 파일의 전체 수치
- PyPI 패키지