# -*- coding: utf-8 -*- import os as _os from pathlib import Path as _P # 이 스크립트는 original/_pipeline/ 에 위치. 코퍼스 루트 = 상위 폴더. ROOT_DIR = _P(__file__).resolve().parent.parent # ...\original DATA_DIR = _P(__file__).resolve().parent / "data" # 생성 데이터(JSON) # API 키: 커밋 금지 파일(law/.secrets.local.md) 또는 환경변수에서 읽는다. def _load_key(name): v = _os.environ.get(name) if v: return v.strip() sec = ROOT_DIR.parent / ".secrets.local.md" if sec.exists(): import re as _re for pat in (r"KCSC[\s\S]*?`([A-Za-z0-9]{20,})`", r"인증키[:\s]*`?([A-Za-z0-9]{30,})`?"): m = _re.search(pat, sec.read_text(encoding="utf-8")) if m: return m.group(1) return "" """HWP5(OLE) 본문 텍스트 추출 — 순수 파이썬. BodyText/Section* 스트림을 (필요시 raw-deflate 해제) 레코드 파싱해 문단 텍스트(HWPTAG_PARA_TEXT)를 UTF-16LE로 뽑는다. """ import sys, re, zlib, struct import olefile HWPTAG_BEGIN = 0x10 HWPTAG_PARA_HEADER = HWPTAG_BEGIN + 50 # 0x42 HWPTAG_PARA_TEXT = HWPTAG_BEGIN + 51 # 0x43 HWPTAG_CTRL_HEADER = HWPTAG_BEGIN + 55 # 0x47 HWPTAG_LIST_HEADER = HWPTAG_BEGIN + 56 # 0x48 HWPTAG_TABLE = HWPTAG_BEGIN + 61 # 0x4d def is_compressed(ole): with ole.openstream("FileHeader") as f: data = f.read() # 36바이트 오프셋의 속성 플래그 bit0 = 압축여부 flags = struct.unpack("> 10) & 0x3FF size = (header >> 20) & 0xFFF if size == 0xFFF: size = struct.unpack("= 8: ncols = struct.unpack(" tbl_level 인 동안, LIST_HEADER마다 새 셀 cells, cur = [], None while j < n and recs[j][1] > tbl_level: t2, l2, d2 = recs[j] if t2 == HWPTAG_LIST_HEADER: if cur is not None: cells.append(cur) cur = "" elif t2 == HWPTAG_PARA_TEXT and cur is not None: seg = para_text(d2).strip() cur = (cur + " " + seg).strip() if cur else seg j += 1 if cur is not None: cells.append(cur) md = _table_md(cells, ncols) if ncols else None if md: items.append(("table", md)) else: for c in cells: if c.strip(): items.append(("text", c)) i = j continue if tag == HWPTAG_PARA_TEXT: s = para_text(data).strip() if s: items.append(("text", s)) i += 1 ole.close() return items if __name__ == "__main__": paras = extract(sys.argv[1]) text = "\n".join(p for p in paras if p) if len(sys.argv) > 2: open(sys.argv[2], "w", encoding="utf-8").write(text) print(f"문단 {len(paras)} / 문자 {len(text)}") print("--- 처음 40줄 ---") for p in [x for x in paras if x][:40]: print(p[:100])