DB_JOB/작업파일/완료/공공기관/_gnbcrawl.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

53 lines
2.7 KiB
Python

# -*- coding: utf-8 -*-
import json
from playwright.sync_api import sync_playwright
SITES=[(2,'강원대학교병원','https://www.knuh.or.kr'),(3,'건강보험심사평가원','https://www.hira.or.kr'),
(5,'게임물관리위원회','https://www.grac.or.kr'),(8,'경제인문사회연구회','https://www.nrc.re.kr'),
(11,'국가생명윤리정책원','https://nibp.kr'),(12,'국가유산진흥원','https://www.kh.or.kr'),
(14,'국립공원공단','https://www.knps.or.kr'),(15,'국립광주과학관','https://www.sciencecenter.or.kr'),
(16,'국립낙동강생물자원관','https://www.nnibr.re.kr'),(17,'국립박물관문화재단','https://www.nmf.or.kr'),
(19,'국립중앙의료원','https://www.nmc.or.kr'),(27,'국악방송','http://www.igbf.kr')]
WALK='''()=>{
const txt=el=>(el.textContent||'').replace(/\\s+/g,' ').trim();
let best=null,bestScore=0;
document.querySelectorAll('ul, nav').forEach(ul=>{
let links=ul.querySelectorAll('a').length;
let nested=ul.querySelectorAll('ul ul').length;
let score = links*(nested>0?2:1);
if(links>=6 && links<500 && score>bestScore){bestScore=score;best=ul;}
});
if(!best) return {err:'none', a:document.querySelectorAll('a').length};
function walk(ul,lvl,out){
[...ul.children].filter(li=>li.tagName==='LI').forEach(li=>{
let a=li.querySelector(':scope > a, :scope > button, :scope > p > a, :scope > div > a');
let t=a?txt(a):'';
let href=a?(a.getAttribute('href')||''):'';
if(t) out.push({lvl, t:t.slice(0,40), href:href.slice(0,80)});
let sub=li.querySelector(':scope > ul');
if(sub) walk(sub,lvl+1,out);
});
return out;
}
return {cls:(typeof best.className==='string'?best.className:''), tree:walk(best,1,[])};
}'''
out={}
with sync_playwright() as p:
b=p.chromium.launch(headless=True); pg=b.new_page()
for no,name,base in SITES:
try:
pg.goto(base, wait_until='domcontentloaded', timeout=35000); pg.wait_for_timeout(2000)
r=pg.evaluate(WALK)
if 'tree' in r:
lvls={}
for x in r['tree']: lvls[x['lvl']]=lvls.get(x['lvl'],0)+1
print('%d.%s | cls=%s | items=%d 깊이분포=%s'%(no,name,r['cls'][:25],len(r['tree']),lvls))
out[no]={'name':name,'base':base,**r}
else:
print('%d.%s | 추출실패 (a=%s)'%(no,name,r.get('a')))
out[no]={'name':name,'base':base,'err':r.get('err')}
except Exception as e:
print('%d.%s | ERR %s'%(no,name,str(e)[:40])); out[no]={'name':name,'base':base,'err':str(e)[:40]}
b.close()
json.dump(out, open('_gnb_trees.json','w',encoding='utf-8'), ensure_ascii=False, indent=1)
print('saved _gnb_trees.json')