공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
109 lines
5.6 KiB
Python
109 lines
5.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""정읍 menuCd LNB 하위메뉴 전수 전개. 부모li(직접<a> menuCd==행, 자식ul>=2)→lc+1 G전개.
|
|
_blank자식=사이트, 게시판키워드=게시판, 그외 페이지M1. F병합·하이퍼링크·새행 파란색.
|
|
사용: python -X utf8 _정읍_LNB하위.py [--write]
|
|
"""
|
|
import sys, os, re, shutil, importlib.util
|
|
from urllib.parse import urljoin
|
|
import openpyxl
|
|
from openpyxl.worksheet.hyperlink import Hyperlink
|
|
from openpyxl.styles import PatternFill
|
|
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'
|
|
BASE='https://www.jeongeup.go.kr'; SESS=M.make_session()
|
|
BLUE=PatternFill(start_color='BDD7EE',end_color='BDD7EE',fill_type='solid')
|
|
BOARD_KW=re.compile(r'공지사항|고시공고|입찰공고|입법예고|자료실|갤러리|보도자료|새소식')
|
|
def muid(u): mm=re.search(r'menuCd=(\w+)',u or ''); return mm.group(1) if mm else None
|
|
def clean(t): return re.sub(r'새창열림|선택됨|\s+',' ',t or '').strip()
|
|
def lc(ws,r):
|
|
for c in range(10,3,-1):
|
|
if ws.cell(r,c).value not in (None,''): return c
|
|
return None
|
|
def children(soup, mid, K):
|
|
for li in soup.find_all('li'):
|
|
da=li.find('a',href=True)
|
|
if not da or muid(da['href'])!=mid: continue
|
|
sub=li.find('ul')
|
|
if not sub: continue
|
|
ks=[(clean(c.find('a').get_text()), urljoin(K,c.find('a')['href']), c.find('a').get('target'))
|
|
for c in sub.find_all('li',recursive=False) if c.find('a',href=True)]
|
|
if len(ks)>=2: return ks, da.get('target')
|
|
return None, None
|
|
def remap_insert(ws, ins, n):
|
|
ranges=[(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))
|
|
ws.insert_rows(ins,n)
|
|
for mc,mr,xc,xr in ranges:
|
|
if mr>=ins: mr+=n; xr+=n
|
|
elif xr>=ins: xr+=n
|
|
ws.merge_cells(start_row=mr,start_column=mc,end_row=xr,end_column=xc)
|
|
def setrow(ws,r,col,label,url,tgt):
|
|
ws.cell(r,col).value=label; ws.cell(r,11).value=url
|
|
if tgt=='_blank':
|
|
ws.cell(r,12).value='사이트'
|
|
for cc in (13,14,15,16,17): ws.cell(r,cc).value=None
|
|
elif BOARD_KW.search(label or ''):
|
|
ws.cell(r,12).value='게시판'; ws.cell(r,13).value=0; ws.cell(r,14).value='어문'
|
|
else:
|
|
ws.cell(r,12).value='페이지'; ws.cell(r,13).value=1; ws.cell(r,14).value='어문'
|
|
def main():
|
|
write='--write' in sys.argv
|
|
wb=openpyxl.load_workbook(XLSX); ws=wb.active
|
|
C=ws.cell(3,3).value
|
|
sheet_mc=set(muid(ws.cell(r,11).value) for r in range(3,ws.max_row+1) if isinstance(ws.cell(r,11).value,str))
|
|
sheet_mc.discard(None)
|
|
plan=[]; skip=[]
|
|
for r in range(3,ws.max_row+1):
|
|
curL=ws.cell(r,12).value
|
|
if curL not in ('페이지','사이트'): continue
|
|
K=ws.cell(r,11).value
|
|
if not(isinstance(K,str) and 'index.jeongeup?menuCd=' in K): continue
|
|
L=lc(ws,r)
|
|
if not L or L>=10 or ws.cell(r,L+1).value not in (None,''): continue
|
|
soup=M.fetch(SESS,K)
|
|
if soup is None: continue
|
|
ks,selftgt=children(soup,muid(K),K)
|
|
if not ks: continue
|
|
if curL=='사이트' and selftgt=='_blank': continue # 진짜 외부사이트(부모자신 새창)=유지
|
|
if muid(ks[0][1])!=muid(K): continue # 부모=첫자식 패턴만
|
|
rest=ks[1:]
|
|
miss=[c for c in rest if muid(c[1]) not in sheet_mc]
|
|
if not miss: continue # 자식 다 존재(형제레벨)→스킵
|
|
if len(miss)!=len(rest): # 부분전개=수동
|
|
skip.append((r,ws.cell(r,L).value,len(miss),len(rest))); continue
|
|
plan.append((r,L,ws.cell(r,L).value,ks))
|
|
for r,lab,mi,tot in skip: print(' 부분전개SKIP r%d %s (누락%d/%d)'%(r,lab,mi,tot))
|
|
print('전개대상 %d개'%len(plan))
|
|
for r,L,lab,ks in plan: print(' r%d %s → %s'%(r,lab,[t for t,_,_ in ks]))
|
|
if not write: return
|
|
shutil.copy(XLSX, XLSX.replace('.xlsx','_backup_LNB하위전.xlsx'))
|
|
tot=0
|
|
for r,L,lab,ks in sorted(plan,key=lambda t:-t[0]):
|
|
setrow(ws,r,L+1,ks[0][0],ks[0][1],ks[0][2]) # 부모행=첫자식(파란X, 기존행)
|
|
rest=ks[1:]; n=len(rest)
|
|
if n:
|
|
remap_insert(ws,r+1,n)
|
|
for i,(clab,curl,ctg) in enumerate(rest):
|
|
rr=r+1+i
|
|
ws.cell(rr,3).value=C
|
|
setrow(ws,rr,L+1,clab,curl,ctg)
|
|
ws.row_dimensions[rr].height=15
|
|
for c in range(2,19): ws.cell(rr,c).fill=BLUE # 새행 파란색
|
|
ws.merge_cells(start_row=r,start_column=L,end_row=r+n,end_column=L)
|
|
tot+=n
|
|
print(' r%d %s +%d'%(r,lab,n))
|
|
cleared=0
|
|
for r in range(3,ws.max_row+1):
|
|
if ws.cell(r,12).value=='사이트':
|
|
if any(ws.cell(r,c).value not in (None,'') for c in (13,14,15,16,17)):
|
|
for c in (13,14,15,16,17): ws.cell(r,c).value=None
|
|
cleared+=1
|
|
for r in range(3,ws.max_row+1):
|
|
v=ws.cell(r,11).value; cell=ws.cell(r,11)
|
|
if isinstance(v,str) and v.startswith('http'): cell.hyperlink=Hyperlink(ref=cell.coordinate,target=v)
|
|
else: cell.hyperlink=None
|
|
wb.save(XLSX); print('적용 +%d행(파란색) · 사이트MNOPQ비움 %d'%(tot,cleared))
|
|
if __name__=='__main__': main()
|