DB_JOB/_스크립트/_재검수_blanksite.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

106 lines
4.6 KiB
Python

# -*- coding: utf-8 -*-
"""미검수 기관 새창열림(target=_blank)→사이트 일괄 (사용자룰 2026-06-04).
각 사이트 GNB/메뉴(홈+샘플 콘텐츠페이지)에서 target=_blank 앵커 수집.
같은 정규화URL에서 _blank 우세(blank수>=일반수)인 것만 '사이트'(내부보드 배너 _blank 오탐 방지).
xlsx의 그 URL 행(현재 페이지/게시판) → L=사이트, M·N 비움.
사용: python -X utf8 _재검수_blanksite.py <기관...> [--write]
"""
import sys, os, re, shutil, importlib.util
from collections import Counter
from urllib.parse import urljoin, urldefrag, urlparse
import openpyxl
# 실게시판 보호: 게시판 엔드포인트 URL이면서 현재 L=게시판이면 사이트로 안 뒤집음
BOARD_URL = re.compile(r'selectBbsNttList|/bbs/|BBSMSTR|board/list|list\.buan|selectBoardList|selectCntrct|selectPric|/list\.do|nttList|selectEminwonNoticeList|selectInhAuthPgmList', re.I)
HERE = os.path.dirname(os.path.abspath(__file__))
MODULES = ['_chungnam_phase234_all.py','_chungbuk_phase234_all.py','_jeonbuk_phase234_all.py']
# 파일문서(_blank여도 보류) 제외 안 함 — 사용자 확정: 새창이면 PDF도 사이트.
def load():
sites,mods={},{}
for f in MODULES:
sp=importlib.util.spec_from_file_location(f[:-3],os.path.join(HERE,f));m=importlib.util.module_from_spec(sp);sp.loader.exec_module(m)
for k,v in m.SITES.items(): sites[k]=v;mods[k]=m
return sites,mods
def mkfetch(M,cfg):
if hasattr(M,'make_session'):
s=M.make_session(weak_ssl=cfg.get('weak_ssl',False)); return lambda u:M.fetch(s,u)
return lambda u:M.fetch(u)
def norm(base,h):
h=urljoin(base,h); h=urldefrag(h)[0]
return h.replace('http://','https://').rstrip('/')
def collect_blank(fetch, sample_urls):
"""샘플 페이지들에서 _blank 우세 정규화URL 집합 반환."""
bl,nb=Counter(),Counter()
pages=0
for su in sample_urls:
soup=fetch(su)
if soup is None: continue
pages+=1
for a in soup.find_all('a',href=True):
h=a['href']
if not h or h.startswith('#') or 'javascript' in h.lower(): continue
key=norm(su,h)
if not key.startswith('http'): continue
if (a.get('target') or '')=='_blank': bl[key]+=1
else: nb[key]+=1
site=set(k for k in bl if bl[k]>=1 and bl[k]>=nb[k])
return site,pages
def run(name,cfg,M,write,minrow=3):
xlsx=cfg['xlsx']
wb=openpyxl.load_workbook(xlsx); ws=wb.active
# 샘플: 홈 + xlsx 상위 데이터 URL 3개(헤더 GNB 확보)
urls=[ws.cell(r,11).value for r in range(3,ws.max_row+1) if isinstance(ws.cell(r,11).value,str) and ws.cell(r,11).value.startswith('http')]
if not urls:
print(f'{name}: URL없음'); return
host=urlparse(urls[0]).netloc; scheme='https'
# 권위 있는 메뉴(GNB)만 사용 — 임의 콘텐츠페이지의 _blank 관련링크 노이즈 차단
samples=[f'{scheme}://{host}/', f'{scheme}://{host}/main/',
f'{scheme}://{host}/main/main.do', f'{scheme}://{host}/index.do']
fetch=mkfetch(M,cfg)
site_urls,pages=collect_blank(fetch,samples)
def lab(r):
for c in range(10,3,-1):
v=ws.cell(r,c).value
if v not in (None,''): return str(v).strip()[:18]
return ''
flips=[]
for r in range(max(3,minrow),ws.max_row+1):
K=ws.cell(r,11).value
if not (isinstance(K,str) and K.startswith('http')): continue
curL=ws.cell(r,12).value
if curL=='사이트': continue
if curL=='게시판' and BOARD_URL.search(K): # 실게시판 보호
continue
if norm(samples[0],K) in site_urls:
flips.append((r,lab(r),curL,K))
if write and flips:
bak=xlsx.replace('.xlsx','_backup_새창사이트전.xlsx')
if not os.path.exists(bak): shutil.copy(xlsx,bak)
for r,l,L,K in flips:
ws.cell(r,12).value='사이트'; ws.cell(r,13).value=None; ws.cell(r,14).value=None
wb.save(xlsx)
print(f'{name}: 메뉴페이지{pages}개·_blank우세링크{len(site_urls)} → 사이트전환 {len(flips)}{"[적용]" if write else "[DRY]"}')
for r,l,L,K in sorted(flips)[:20]:
print(f' r{r} {L}→사이트 {l} | {K[:58]}')
if len(flips)>20: print(f' …외 {len(flips)-20}')
def main():
args=sys.argv[1:]; write='--write' in args
minrow=3
for a in args:
if a.startswith('--minrow='): minrow=int(a.split('=')[1])
names=[a for a in args if not a.startswith('--')]
sites,mods=load()
for n in names:
if n in sites: run(n,sites[n],mods[n],write,minrow)
else: print(f'{n}: 없음')
if __name__=='__main__': main()