PDF 챕터별 자동 분할 — 발표 자료 만들 때 한 챕터만 추출
리버싱 핵심원리 PDF 600페이지 중 20장 인라인 패치 부분만 따로 빼고 싶었다. 5분 스크립트로 끝.
발표 준비할 때 두꺼운 PDF에서 한 챕터만 추출해야 하는 경우가 자주 있다. “리버싱 핵심원리” 책 PDF에서 20장 인라인 패치 부분만 따로 빼서 발표 자료에 넣는 식.
수동으로 페이지 번호 일일이 적어서 나누는 건 노답. 5분이면 끝나는 스크립트 쓰자.
한 파일 스크립트
#!/usr/bin/env python3
"""
PDF를 목차(bookmark)별로 분할.
사용: python split_pdf.py input.pdf
"""
import sys
import re
from pathlib import Path
from pypdf import PdfReader, PdfWriter
def safe_filename(s: str) -> str:
s = re.sub(r'[<>:"/\\|?*]', '_', s)
return re.sub(r'\s+', '_', s.strip())[:80]
def split_by_outline(pdf_path: Path, out_dir: Path):
reader = PdfReader(pdf_path)
out_dir.mkdir(parents=True, exist_ok=True)
# 최상위 북마크만 (챕터 단위)
outline = reader.outline
chapters = []
for item in outline:
if isinstance(item, list):
continue # 하위 섹션은 스킵
page_num = reader.get_destination_page_number(item)
chapters.append((item.title, page_num))
# 챕터별 페이지 범위
for i, (title, start) in enumerate(chapters):
end = chapters[i+1][1] if i+1 < len(chapters) else len(reader.pages)
writer = PdfWriter()
for p in range(start, end):
writer.add_page(reader.pages[p])
out_name = f"{i+1:02d}_{safe_filename(title)}.pdf"
with open(out_dir / out_name, 'wb') as f:
writer.write(f)
print(f" → {out_name} (p.{start+1}-{end})")
if __name__ == '__main__':
src = Path(sys.argv[1])
out = src.parent / src.stem
split_by_outline(src, out)
pip install pypdf
python split_pdf.py 리버싱_핵심원리.pdf
결과:
리버싱_핵심원리/
├── 01_1장_리버싱_입문.pdf (p.1-23)
├── 02_2장_Hello_World.pdf (p.24-58)
├── ...
├── 20_20장_인라인_패치_실습.pdf (p.487-512)
└── ...
왜 pypdf인가
대안과 비교:
| 라이브러리 | 의존성 | 속도 | 북마크 지원 |
|---|---|---|---|
pypdf | 순수 Python | 느림 | ✓ |
pdfplumber | 큼 | 보통 | △ |
PyMuPDF (fitz) | C 바인딩 | 빠름 | ✓ |
pdftk (외부 CLI) | Java | 빠름 | ✓ |
pypdf는 의존성 없는 게 강점. 5분 짜리 스크립트엔 이게 정답.
큰 PDF(>500MB) 자주 쓰면 PyMuPDF로 바꾸면 10배 빨라진다.
북마크 없는 PDF는?
스캔본 PDF 등 북마크가 없으면 위 스크립트 안 통한다. 두 가지 옵션:
- 수동 페이지 범위로 자르기 —
writer.add_page(reader.pages[p])에서 p 범위만 박으면 됨 - OCR + 패턴 매칭 — 각 페이지 첫 줄을 OCR해서 “20장” 같은 헤더로 시작하는 페이지 자동 감지.
pytesseract+ 정규식.
대부분의 e-book/교과서 PDF는 북마크가 있어서 1번 옵션도 거의 쓸 일 없다.
잡힌 함정
한글 파일명 깨짐. macOS는 NFD, 윈도우/리눅스는 NFC. safe_filename에서 unicodedata.normalize('NFC', s) 추가하면 안전.
중첩된 outline 구조. PyPDF의 outline은 챕터 안의 섹션이 list로 중첩돼 들어 있다. isinstance(item, list)로 스킵하면 챕터 단위만 추출.
마지막 챕터 페이지 끝. i+1 < len(chapters)가 빠지면 IndexError. else len(reader.pages)로 처리.
한 줄 결론
PDF 챕터별 분할은 GUI 도구 깔지 말고 위 30줄로 끝낸다.
python split_pdf.py 책.pdf
도구를 만드는 데 5분, 그 후엔 30초씩 절약. ROI가 높은 자동화는 작은 데서 시작한다.