- 공공기관2/3 작업본 + 오늘 제출 17곳 D~J 카테고리 셀병합 정상화 - 한국지역난방공사 옵션2(고아셀 F98 수정)+전행 높이17 - 제출_프리랜서2_2026-06-21.zip 생성(17개 xlsx, 2,468행) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
114 lines
4.8 KiB
Python
114 lines
4.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""13개 사이트 GNB 메가메뉴 자동탐지 프로빙 → _gnb/probe.json + 콘솔요약."""
|
|
import sys, io, os, json, re, warnings
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
warnings.filterwarnings('ignore')
|
|
from playwright.sync_api import sync_playwright
|
|
|
|
GNB_DIR = r"D:\01.프로젝트\DB수집\작업파일\공공기관3\_gnb"
|
|
os.makedirs(GNB_DIR, exist_ok=True)
|
|
|
|
SITES = {
|
|
1: ("한국원자력환경공단", "https://www.korad.or.kr"),
|
|
2: ("한국의료분쟁조정중재원", "https://www.k-medi.or.kr"),
|
|
3: ("한국의약품안전관리원", "https://www.drugsafe.or.kr"),
|
|
4: ("한국인터넷진흥원", "https://www.kisa.or.kr"),
|
|
5: ("한국임업진흥원", "https://www.kofpi.or.kr"),
|
|
6: ("한국자산관리공사", "https://www.kamco.or.kr"),
|
|
7: ("한국잡월드", "https://www.koreajobworld.or.kr"),
|
|
8: ("한국장기조직기증원", "https://www.koda1458.kr"),
|
|
9: ("한국장애인개발원", "https://www.koddi.or.kr"),
|
|
10: ("한국장애인고용공단", "https://www.kead.or.kr"),
|
|
11: ("한국장학재단", "https://www.kosaf.go.kr"),
|
|
12: ("한국재정정보원", "https://www.fis.kr"),
|
|
13: ("한국저작권보호원", "https://www.kcopa.or.kr"),
|
|
}
|
|
|
|
# 후보 GNB 컨테이너 셀렉터 (계층 앵커 최다인 것 선택)
|
|
CAND_SELS = ["#gnb","ul#gnb","#gnbmenu",".gnb",".gnb_area",".gnb_wrap","nav .gnb",
|
|
"#header nav ul","header nav","#nav",".nav",".lnb_all",".allmenu",
|
|
".all_menu","#allMenu",".total_menu",".totalmenu",".menu_all",
|
|
"ul.depth1","ul.dep1",".gnb ul",".navi",".navbar","#snb"]
|
|
|
|
PROBE_JS = """
|
|
(sels) => {
|
|
function topUls(el){
|
|
return [...el.querySelectorAll('ul')].filter(ul=>{
|
|
let p=ul.parentElement;
|
|
while(p&&p!==el){ if(p.tagName==='UL') return false; p=p.parentElement; }
|
|
return true; });
|
|
}
|
|
function countTree(el){
|
|
// count anchors with text inside nested uls (proxy for menu richness) + max depth
|
|
let n=0, md=0;
|
|
function walk(ul, depth){
|
|
[...ul.children].filter(li=>li.tagName==='LI').forEach(li=>{
|
|
let a=null;
|
|
for(const x of li.querySelectorAll('a')){
|
|
let p=x.parentElement, inside=false;
|
|
while(p&&p!==li){ if(p.tagName==='UL'){inside=true;break;} p=p.parentElement;}
|
|
if(!inside && (x.textContent||'').trim()){a=x;break;}
|
|
}
|
|
if(a){ n++; md=Math.max(md,depth); }
|
|
topUls(li).forEach(s=>walk(s,depth+1));
|
|
});
|
|
}
|
|
(el.tagName==='UL'?[el]:topUls(el)).forEach(u=>walk(u,1));
|
|
return {n, md};
|
|
}
|
|
const out=[];
|
|
for(const sel of sels){
|
|
let best=null;
|
|
document.querySelectorAll(sel).forEach(el=>{
|
|
const r=countTree(el);
|
|
if(!best || r.n>best.n) best=r;
|
|
});
|
|
if(best && best.n>=3) out.push({sel, n:best.n, md:best.md});
|
|
}
|
|
out.sort((a,b)=> (b.n*10+b.md)-(a.n*10+a.md));
|
|
// d1 labels of the winner
|
|
let d1=[];
|
|
if(out.length){
|
|
const el=document.querySelector(out[0].sel);
|
|
const uls = el.tagName==='UL'?[el]:topUls(el);
|
|
uls.forEach(u=>[...u.children].filter(li=>li.tagName==='LI').forEach(li=>{
|
|
for(const a of li.querySelectorAll('a')){
|
|
let p=a.parentElement, inside=false;
|
|
while(p&&p!==li){ if(p.tagName==='UL'){inside=true;break;} p=p.parentElement;}
|
|
if(!inside && (a.textContent||'').trim()){ d1.push(a.textContent.trim().replace(/\\s+/g,' ')); break;}
|
|
}
|
|
}));
|
|
}
|
|
return {cands: out.slice(0,5), d1: d1.slice(0,15)};
|
|
}
|
|
"""
|
|
|
|
def run():
|
|
res={}
|
|
with sync_playwright() as p:
|
|
br=p.chromium.launch(headless=True)
|
|
ctx=br.new_context(ignore_https_errors=True, viewport={'width':1920,'height':1080},
|
|
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36')
|
|
for num,(name,base) in SITES.items():
|
|
pg=ctx.new_page()
|
|
info={'num':num,'name':name,'base':base}
|
|
try:
|
|
pg.goto(base+'/', wait_until='domcontentloaded', timeout=35000)
|
|
pg.wait_for_timeout(2800)
|
|
r=pg.evaluate(PROBE_JS, CAND_SELS)
|
|
info.update(r); info['ok']=True
|
|
info['final_url']=pg.url
|
|
except Exception as e:
|
|
info['ok']=False; info['err']=f'{type(e).__name__}:{e}'
|
|
res[num]=info
|
|
pg.close()
|
|
c=info.get('cands',[])
|
|
top=c[0] if c else None
|
|
print(f"[{num:2}.{name}] {'OK' if info.get('ok') else 'FAIL'} top={top} d1#={len(info.get('d1',[]))}")
|
|
br.close()
|
|
io.open(os.path.join(GNB_DIR,'probe.json'),'w',encoding='utf-8').write(json.dumps(res,ensure_ascii=False,indent=1))
|
|
print('saved probe.json')
|
|
|
|
if __name__=='__main__':
|
|
run()
|