# -*- coding: utf-8 -*- """E열(대분류) 병합 누락 보정 — 재크롤링 없이 기존 엑셀의 D/E/F 병합만 바로잡는다. 원인: phase1이 D를 먼저 병합 → 블록 2행부터 D=None → E 병합(같은 D 안에서만)이 D블록 첫 행에서 끊겨 첫 행 E가 단독으로 남음. 해결: 현재 병합에서 값 복원 → D/E/F 병합 해제 → 전 행에 값 채움 → F → E → D 순서로 재병합(컬럼을 병합하면 그 컬럼 2행부터 None이 되므로 순서가 중요). 데이터(D/E/F 텍스트)는 그대로. 헤더 병합(B1:R1 등)은 건드리지 않음. 파일별 *_backup_emerge전.xlsx 백업. """ import sys, os, glob, shutil, warnings, openpyxl from openpyxl.styles import Alignment from openpyxl.utils import get_column_letter sys.stdout.reconfigure(encoding='utf-8') warnings.filterwarnings('ignore') START = 3 COLS = (4, 5, 6) # D, E, F CENTER = Alignment(horizontal='center', vertical='center', wrap_text=False) def filled_values(ws, col): """현재 병합 상태에서 각 행의 실제 값을 복원(병합 top값으로 채움).""" end = ws.max_row vals = {r: ws.cell(r, col).value for r in range(START, end + 1)} for mr in ws.merged_cells.ranges: if mr.min_col == col == mr.max_col and mr.min_row >= START: top = ws.cell(mr.min_row, col).value for r in range(mr.min_row, mr.max_row + 1): vals[r] = top return vals, end def merge_runs(ws, col, end, group_keys, fills): """fills[col][r] 기준으로 연속 동일 구간 병합. group_keys: 상위 컬럼 튜플.""" letter = get_column_letter(col) runs = [] def key(r): return tuple(fills[g][r] for g in group_keys) cur_val = fills[col][START] cur_grp = key(START) run_start = START for r in range(START + 1, end + 1): v, g = fills[col][r], key(r) if v == cur_val and g == cur_grp: continue if cur_val not in (None, '') and r - 1 > run_start: runs.append((run_start, r - 1)) cur_val, cur_grp, run_start = v, g, r if cur_val not in (None, '') and end > run_start: runs.append((run_start, end)) for s, e in runs: ws.merge_cells(f'{letter}{s}:{letter}{e}') ws.cell(s, col).alignment = CENTER return len(runs) def fix(xlsx): wb = openpyxl.load_workbook(xlsx) ws = wb.active # 1) 값 복원 fills, end = {}, None for c in COLS: fills[c], end = filled_values(ws, c) # 2) D/E/F 데이터 병합만 해제 (단일컬럼·row>=START) to_unmerge = [str(mr) for mr in ws.merged_cells.ranges if mr.min_col == mr.max_col and mr.min_col in COLS and mr.min_row >= START] for rng in to_unmerge: ws.unmerge_cells(rng) # 3) 전 행에 값 다시 기입 for c in COLS: for r in range(START, end + 1): ws.cell(r, c).value = fills[c][r] # 4) F → E → D 순서 재병합 n_f = merge_runs(ws, 6, end, (4, 5), fills) n_e = merge_runs(ws, 5, end, (4,), fills) n_d = merge_runs(ws, 4, end, (), fills) return wb, (n_d, n_e, n_f, len(to_unmerge)) def main(): sel = [a for a in sys.argv[1:] if not a.startswith('-')] files = [] for region in ['충청남도', '충청북도']: for d in sorted(glob.glob(region + r'\*')): if not os.path.isdir(d): continue base = os.path.basename(d) nm = base.split('.', 1)[1] if '.' in base else base prov = os.path.basename(os.path.dirname(d)) f = os.path.join(d, f'{prov}_{nm}.xlsx') if os.path.exists(f) and (not sel or nm in sel): files.append((nm, f)) print(f'대상 {len(files)}개: {[n for n, _ in files]}') ok = [] for nm, f in files: try: bak = f.replace('.xlsx', '') + '_backup_emerge전.xlsx' if not os.path.exists(bak): shutil.copy2(f, bak) wb, (nd, ne, nf, un) = fix(f) wb.save(f) print(f' ✅ {nm:<6} 병합해제 {un:>3} → 재병합 D:{nd} E:{ne} F:{nf}') ok.append(nm) except PermissionError: print(f' ⚠️ {nm:<6} 파일 잠김(열려있음) → 건너뜀') except Exception as e: import traceback; traceback.print_exc() print(f' !! {nm:<6} 오류: {e}') print(f'\n완료 {len(ok)}/{len(files)}') if __name__ == '__main__': main()