공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
125 lines
5.7 KiB
Python
125 lines
5.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""정읍 col탭 후처리(단일패스): ①G병합+H첫행 빈 껍데기 처리 ②col탭 자식 L 재판별(게시판).
|
|
- 문제그룹 = G(7)병합인데 첫행 H(8)가 빈 그룹.
|
|
- 자식URL 전부 javascript:void/빈 = JS필터탭 → 자식행 삭제, 랜딩만 단일행(M=탭수).
|
|
- 자식URL 실 menuCd = 진짜페이지 → 빈H 랜딩행 삭제(중복), 자식 승격·G재병합·L재판별.
|
|
- 강좌정보그룹(H첫행 채워짐)은 자식 L재판별만.
|
|
사용: python -X utf8 _정읍_coltab_fix.py [--write]
|
|
"""
|
|
import sys, os, re, shutil, importlib.util
|
|
import openpyxl
|
|
from openpyxl.worksheet.hyperlink import Hyperlink
|
|
|
|
HERE=os.path.dirname(os.path.abspath(__file__))
|
|
s=importlib.util.spec_from_file_location('m',os.path.join(HERE,'_jeonbuk_phase234_all.py'))
|
|
M=importlib.util.module_from_spec(s); s.loader.exec_module(M)
|
|
XLSX=r'D:\01.프로젝트\DB수집\작업파일\광역_사이트맵\전북특별자치도\13.정읍시\전북특별자치도_정읍시.xlsx'
|
|
SESS=M.make_session()
|
|
TOT=re.compile(r'전체\s*([\d,]+)|총\s*([\d,]+)\s*건|총\s*게시물\s*([\d,]+)')
|
|
LIST_SEL='table tbody tr, .bbs_list li, .board_list li, ul.list li, .gallery li, .photo_list li, .basic_list li'
|
|
PG_SEL='.paging a, .pagination a, .page a'
|
|
|
|
def isreal(u): return isinstance(u,str) and u.startswith('http')
|
|
def board_detect(K):
|
|
soup=None
|
|
for _ in range(3):
|
|
soup=M.fetch(SESS,K)
|
|
if soup is not None: break
|
|
if soup is None: return None
|
|
b=M.get_body(soup,M.BODY_SEL); txt=b.get_text(' ',strip=True)
|
|
lr=len([li for li in b.select(LIST_SEL) if li.find('a',href=True)])
|
|
pg=len([a for a in b.select(PG_SEL) if a.get_text(strip=True).isdigit()])
|
|
mm=TOT.search(txt)
|
|
if lr>=3 or pg>=2 or mm:
|
|
if mm:
|
|
g=[x for x in mm.groups() if x]; return ('게시판', int(g[0].replace(',','')))
|
|
return ('게시판', lr)
|
|
return ('페이지', 1)
|
|
|
|
def main():
|
|
write='--write' in sys.argv
|
|
wb=openpyxl.load_workbook(XLSX); ws=wb.active
|
|
|
|
gmerges=[(x.min_row,x.max_row) for x in ws.merged_cells.ranges if x.min_col==7==x.max_col and x.max_row>x.min_row]
|
|
real_child_rows=[]; js_groups=[]; shell_real=[]
|
|
for a,b in sorted(gmerges):
|
|
hfirst_empty = ws.cell(a,8).value in (None,'')
|
|
child_urls=[ws.cell(r,11).value for r in range(a+1,b+1)]
|
|
all_js = len(child_urls)>0 and all((not isreal(u)) for u in child_urls)
|
|
if hfirst_empty and all_js: js_groups.append((a,b))
|
|
elif hfirst_empty:
|
|
shell_real.append((a,b))
|
|
for r in range(a+1,b+1):
|
|
if isreal(ws.cell(r,11).value): real_child_rows.append((r,ws.cell(r,11).value))
|
|
else:
|
|
for r in range(a,b+1):
|
|
if ws.cell(r,8).value not in (None,'') and isreal(ws.cell(r,11).value):
|
|
real_child_rows.append((r,ws.cell(r,11).value))
|
|
|
|
print('=== JS필터그룹(가짜탭제거):',[(a,b,ws.cell(a,7).value) for a,b in js_groups])
|
|
print('=== 실URL껍데기(랜딩삭제):',[(a,b,ws.cell(a,7).value) for a,b in shell_real])
|
|
detected={}
|
|
for r,K in real_child_rows:
|
|
d=board_detect(K)
|
|
if d: detected[K]=d
|
|
print(' r%d %s → %s'%(r,ws.cell(r,8).value or ws.cell(r,7).value,d))
|
|
if not write:
|
|
print('\n[미적용] --write 로 실행'); return
|
|
try: shutil.copy(XLSX, XLSX.replace('.xlsx','_backup_coltabfix전.xlsx'))
|
|
except PermissionError: print('⚠ 파일 열림 - 닫고 재실행'); return
|
|
|
|
# 1) L/M 재판별 적용 (현재 행에, URL기준)
|
|
nL=0
|
|
for r in range(3,ws.max_row+1):
|
|
K=ws.cell(r,11).value
|
|
if isreal(K) and K in detected:
|
|
L,Mv=detected[K]; curL=ws.cell(r,12).value
|
|
if curL!=L:
|
|
ws.cell(r,12).value=L; nL+=1
|
|
if L=='게시판':
|
|
cur=ws.cell(r,13).value if isinstance(ws.cell(r,13).value,int) else 0
|
|
ws.cell(r,13).value=max(Mv,cur)
|
|
print('L 재판별 변경 %d행'%nL)
|
|
|
|
# 2) 삭제 행집합 + JS랜딩 M
|
|
D=set(); jslandings=[] # (row, mcount)
|
|
for a,b in js_groups:
|
|
for r in range(a+1,b+1): D.add(r)
|
|
ws.cell(a,13).value=b-a # 랜딩 M=탭수 (삭제 전 적용)
|
|
jslandings.append((a,b-a))
|
|
shell_glabels=[] # (first_surviving_child_orig_row, glabel)
|
|
for a,b in shell_real:
|
|
D.add(a)
|
|
shell_glabels.append((a+1, ws.cell(a,7).value))
|
|
|
|
Dsorted=sorted(D)
|
|
def shift(r): return r - sum(1 for d in Dsorted if d<r)
|
|
|
|
# 3) 전체 unmerge 1회 → 행삭제(내림차순) → 전역보정 재병합
|
|
orig_merges=[(x.min_col,x.min_row,x.max_col,x.max_row) for x in list(ws.merged_cells.ranges)]
|
|
for x in list(ws.merged_cells.ranges): ws.unmerge_cells(str(x))
|
|
for r in sorted(D,reverse=True): ws.delete_rows(r,1)
|
|
for c1,r1,c2,r2 in orig_merges:
|
|
surv=[r for r in range(r1,r2+1) if r not in D]
|
|
if not surv: continue
|
|
nr1=shift(surv[0]); nr2=shift(surv[-1])
|
|
if nr2>nr1 or c2>c1:
|
|
try: ws.merge_cells(start_row=nr1,start_column=c1,end_row=nr2,end_column=c2)
|
|
except Exception as e: print('merge skip',(c1,nr1,c2,nr2),e)
|
|
|
|
# 4) 껍데기 승격행 G라벨 재기입 (삭제로 유실됨)
|
|
for orig_first, glab in shell_glabels:
|
|
nr=shift(orig_first)
|
|
ws.cell(nr,7).value=glab
|
|
print(' 껍데기→ G라벨 "%s" @ new r%d'%(glab,nr))
|
|
|
|
# 5) 하이퍼링크 재구성
|
|
for r in range(3,ws.max_row+1):
|
|
v=ws.cell(r,11).value; cell=ws.cell(r,11)
|
|
if isreal(v): cell.hyperlink=Hyperlink(ref=cell.coordinate,target=v)
|
|
else: cell.hyperlink=None
|
|
wb.save(XLSX)
|
|
print('저장 완료. 삭제 %d행, max_row=%d'%(len(D),ws.max_row))
|
|
|
|
if __name__=='__main__': main()
|