DB_JOB/작업파일/완료/광역_사이트맵/_fix_external.py
hehihoho3 df16c98366 백업: DB수집 전체 스냅샷 (공공기관2 정리 전)
공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 18:15:40 +09:00

183 lines
7.0 KiB
Python

# -*- coding: utf-8 -*-
"""외부 URL인데 사이트 아님 정리 (충남/충북/제주, 검수완료 제외).
- 외부도메인(기관 core 도메인이 host에 없음) & L!=사이트 인 행:
· D/E/F/G 라벨 전부 빈칸 → 템플릿 잔재 → 삭제
· 라벨 있음 → 진짜 외부링크 → L=사이트, M/N/O/P/Q 비움, S=외부링크
flatten→잔재제외+사이트적용→재작성→D/E/F 재병합→K하이퍼링크.
"""
import sys, glob, os, re, shutil, warnings
from copy import copy
from urllib.parse import urlsplit
from collections import Counter
import openpyxl
from openpyxl.styles import Alignment, Font
warnings.filterwarnings('ignore')
MAXCOL = 27
HEADER_MERGES = {'B1:R1', 'S1:W1', 'Y1:AA1'}
EXCLUDE = {'계룡시', '공주시', '금산군', '논산시', '보령시'}
def host_of(u):
try:
return urlsplit(u).netloc.lower()
except Exception:
return ''
def process(path, write):
wb = openpyxl.load_workbook(path)
ws = wb.active
# flatten cat for label check + main domain
fcat = {}
for r in range(3, ws.max_row + 1):
fcat[r] = {c: ws.cell(r, c).value for c in (4, 5, 6, 7)}
for mr in ws.merged_cells.ranges:
if mr.min_col in (4, 5, 6) and mr.min_row >= 3:
top = ws.cell(mr.min_row, mr.min_col).value
for rr in range(mr.min_row, mr.max_row + 1):
if rr in fcat and fcat[rr].get(mr.min_col) in (None, ''):
fcat[rr][mr.min_col] = top
hosts = Counter()
for r in range(3, ws.max_row + 1):
if ws.cell(r, 2).value:
k = ws.cell(r, 11).value
if isinstance(k, str) and k.startswith('http'):
hosts[host_of(k)] += 1
if not hosts:
return (0, 0)
core = re.sub(r'^www\.', '', hosts.most_common(1)[0][0]).split('.')[0]
delete = set()
sitemark = set()
for r in range(3, ws.max_row + 1):
if not ws.cell(r, 2).value:
continue
k = ws.cell(r, 11).value
L = ws.cell(r, 12).value
if isinstance(k, str) and k.startswith('http'):
h = host_of(k)
if core and core not in h and L != '사이트':
haslabel = any(fcat[r].get(c) not in (None, '') for c in (4, 5, 6, 7))
if haslabel:
sitemark.add(r)
else:
delete.add(r)
if not delete and not sitemark:
return (0, 0)
if not write:
return (len(delete), len(sitemark))
shutil.copy(path, path.replace('.xlsx', '_backup_외부정리전.xlsx'))
# flatten
for mr in list(ws.merged_cells.ranges):
s = str(mr)
if s in HEADER_MERGES:
continue
top = ws.cell(mr.min_row, mr.min_col).value
ws.unmerge_cells(s)
for rr in range(mr.min_row, mr.max_row + 1):
for cc in range(mr.min_col, mr.max_col + 1):
if ws.cell(rr, cc).value in (None, ''):
ws.cell(rr, cc).value = top
out = []
for r in range(3, ws.max_row + 1):
if all(ws.cell(r, c).value in (None, '') for c in range(2, MAXCOL + 1)):
continue
if r in delete:
continue
vals = {c: ws.cell(r, c).value for c in range(1, MAXCOL + 1)}
if r in sitemark:
vals[12] = '사이트' # L
vals[13] = None # M
vals[14] = None # N
vals[15] = None # O
vals[16] = None # P
vals[17] = None # Q
vals[19] = '외부링크' # S
styles = {c: (copy(ws.cell(r, c).font), copy(ws.cell(r, c).fill),
copy(ws.cell(r, c).border), copy(ws.cell(r, c).alignment),
ws.cell(r, c).number_format, copy(ws.cell(r, c).protection))
for c in range(1, MAXCOL + 1)}
out.append({'vals': vals, 'styles': styles})
for r in range(3, ws.max_row + 1):
for c in range(1, MAXCOL + 1):
ws.cell(r, c).value = None
ws.cell(r, c).hyperlink = None
START = 3
for i, orow in enumerate(out):
rr = START + i
for c in range(1, MAXCOL + 1):
f, fl, bd, al, nf, pr = orow['styles'][c]
cell = ws.cell(rr, c)
cell.font = copy(f); cell.fill = copy(fl); cell.border = copy(bd)
cell.alignment = copy(al); cell.number_format = nf; cell.protection = copy(pr)
cell.value = orow['vals'].get(c)
ws.cell(rr, 2).value = i + 1
END = START + len(out) - 1
for rr in range(END + 1, ws.max_row + 1):
for c in range(1, MAXCOL + 1):
ws.cell(rr, c).value = None
ws.cell(rr, c).hyperlink = None
center = Alignment(horizontal='center', vertical='center', wrap_text=True)
left = Alignment(horizontal='left', vertical='center', wrap_text=False)
def merge_runs(letter, idx, gc=()):
cur = ws.cell(START, idx).value
grp = tuple(ws.cell(START, g).value for g in gc)
st = START; runs = []
for rr in range(START + 1, END + 1):
val = ws.cell(rr, idx).value
g2 = tuple(ws.cell(rr, gg).value for gg in gc)
if val == cur and g2 == grp:
continue
if cur not in (None, '') and rr - 1 > st:
runs.append((st, rr - 1))
cur, grp, st = val, g2, rr
if cur not in (None, '') and END > st:
runs.append((st, END))
for s, e in runs:
ws.merge_cells(f'{letter}{s}:{letter}{e}')
ws.cell(s, idx).alignment = center
merge_runs('F', 6, (4, 5))
merge_runs('E', 5, (4,))
merge_runs('D', 4)
for rr in range(START, END + 1):
for c in (4, 5, 6, 7):
if ws.cell(rr, c).value is not None:
ws.cell(rr, c).alignment = center
for rr in range(1, END + 1):
ws.row_dimensions[rr].height = 15
for rr in range(START, END + 1):
cell = ws.cell(rr, 11)
u = cell.value
if isinstance(u, str) and u.startswith(('http://', 'https://')):
cell.hyperlink = u
old = cell.font
cell.font = Font(name=old.name or '맑은 고딕', size=old.size or 11,
bold=old.bold, italic=old.italic, color='0000FF', underline='single')
cell.alignment = left
wb.save(path)
return (len(delete), len(sitemark))
def main():
write = '--write' in sys.argv
regions = ['충청남도', '충청북도', '제주특별자치도']
base = os.path.dirname(os.path.abspath(__file__))
for region in regions:
for path in sorted(glob.glob(os.path.join(base, region, '*', '*.xlsx'))):
if 'backup' in path or '_r2' in path:
continue
nm = os.path.basename(path).replace('.xlsx', '')
if nm in EXCLUDE:
continue
d, s = process(path, write)
if d or s:
tag = '저장' if write else 'dry'
print(f'[{tag}] {nm}: 삭제(잔재) {d} / 사이트표시 {s}')
if __name__ == '__main__':
main()