# -*- coding: utf-8 -*- # 청양 본문탭 +29 전개 + 고아5삭제. fill-down 방식(전행 풀path→consecutive-equal 병합). 1~122 불변. import openpyxl, json, io, sys, shutil from copy import copy from collections import Counter sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8') F=r'D:\01.프로젝트\DB수집\작업파일\광역_사이트맵\충청남도\13.청양군\충청남도_청양군.xlsx' BK=r'D:\01.프로젝트\DB수집\작업파일\광역_사이트맵\충청남도\13.청양군\충청남도_청양군_backup_본문탭전개2전.xlsx' shutil.copyfile(F,BK); print('백업',BK) miss=json.load(open('_cy3_missing.json',encoding='utf-8')) wb=openpyxl.load_workbook(F); ws=wb.active NCOL=17 last=ws.max_row # 1) 병합맵 → filled[(r,c)] filled={} mranges=list(ws.merged_cells.ranges) def anchor_val(r,c): for m in mranges: if m.min_row<=r<=m.max_row and m.min_col<=c<=m.max_col: return ws.cell(m.min_row,m.min_col).value return ws.cell(r,c).value for r in range(123,last+1): for c in range(4,9): filled[(r,c)]=anchor_val(r,c) def style_of(r,c): cell=ws.cell(r,c) return dict(font=copy(cell.font),border=copy(cell.border),fill=copy(cell.fill), alignment=copy(cell.alignment),number_format=cell.number_format,protection=copy(cell.protection)) def cstyle(st): return {k:(copy(v) if k!='number_format' else v) for k,v in st.items()} def apply_style(cell,st): cell.font=st['font']; cell.border=st['border']; cell.fill=st['fill'] cell.alignment=st['alignment']; cell.number_format=st['number_format']; cell.protection=st['protection'] ORPHAN=set(range(386,391)) # 부모 행(B순번)별 자식 PLAN={ # anchor B순번 -> (childcol, parent_set{col:val}, parent_Lfix, miss_key) 135:(7,{7:'청양사랑기부제 안내'},'페이지','133'), 248:(7,{},None,'243'), 250:(7,{7:'인구증가 시책'},None,'246'), 296:(8,{8:'고추'},None,'291'), 297:(8,{8:'전통주(구기주/생막걸리)'},'페이지','292'), } rowh=ws.row_dimensions[200].height or 15 # 2) 기존행 캡처(filled D-H + 기타 그대로) def cap_row(r): vals={c:ws.cell(r,c).value for c in range(2,NCOL+1)} for c in range(4,9): vals[c]=filled[(r,c)] # D-H를 filled로 hl=ws.cell(r,11).hyperlink return {'b':vals[2],'vals':vals,'st':{c:style_of(r,c) for c in range(2,NCOL+1)},'hyper':(hl.target if hl else None)} old=[] for r in range(123,last+1): if r in ORPHAN: continue rec=cap_row(r) b=rec['b'] if b in PLAN: col,pset,Lfix,_=PLAN[b] for c,v in pset.items(): rec['vals'][c]=v if Lfix: rec['vals'][12]=Lfix old.append(rec) # 3) 새 리스트 + 자식 삽입(filled path = 부모 filled 기반) def make_child(parentrec,col,lab,url,blank): vals={c:None for c in range(2,NCOL+1)} vals[3]='충청남도 청양군' # filled D-H 상속: 부모의 filled D..(col-1) 복사, col=lab, 더깊은=None for c in range(4,col): vals[c]=parentrec['vals'][c] vals[col]=lab vals[11]=url if blank: vals[12]='사이트' else: vals[12]='페이지'; vals[13]=1; vals[14]='어문' st={c:cstyle(parentrec['st'][c]) for c in range(2,NCOL+1)} return {'b':None,'vals':vals,'st':st,'hyper':url if url and str(url).startswith('http') else None} newlist=[]; added=0 for rec in old: newlist.append(rec) b=rec['b'] if b in PLAN: col,_,_,mk=PLAN[b] for x in miss[mk]['miss']: newlist.append(make_child(rec,col,x['lab'],x['absu'],x['blank'])); added+=1 print('기존',len(old),'신규',added,'합',len(newlist)) # 4) 123+ 언머지 + 삭제 for m in list(ws.merged_cells.ranges): if m.min_row>=123: ws.unmerge_cells(str(m)) ws.delete_rows(123,last-123+1) # 5) 쓰기(filled 전값) for i,rec in enumerate(newlist): r=123+i for c in range(2,NCOL+1): cell=ws.cell(r,c); cell.value=rec['vals'][c]; apply_style(cell,rec['st'][c]) ws.row_dimensions[r].height=rowh if rec['hyper']: ws.cell(r,11).hyperlink=rec['hyper'] newlast=122+len(newlist) # 6) 병합 재계산: consecutive-equal per col 4-8 def val(r,c): v=ws.cell(r,c).value return v if (v is not None and str(v).strip()!='') else None for c in range(4,9): r=123 while r<=newlast: v=val(r,c) if v is None: r+=1; continue r2=r while r2+1<=newlast and val(r2+1,c)==v: r2+=1 if r2>r: ws.merge_cells(start_row=r,start_column=c,end_row=r2,end_column=c) r=r2+1 wb.save(F) print('저장 newlast',newlast,'max_row',ws.max_row) d=Counter(ws.cell(r,12).value for r in range(123,newlast+1)) print('123+ L',dict(d))