공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
106 lines
4.6 KiB
Python
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()
|