# -*- coding: utf-8 -*- """주택금융 전 페이지에서 con0X/row 탭(별도 .do 서브페이지 탭) 스캔. 시트에 없는 탭멤버 리포트.""" import sys, io, os, glob, re, json sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') import openpyxl from playwright.sync_api import sync_playwright BASE = r"D:\01.프로젝트\DB수집\작업파일\공공기관2" fp = [x for x in glob.glob(os.path.join(BASE, "5.*", "*.xlsx")) if "_backup" not in x and "~$" not in x][0] ws = openpyxl.load_workbook(fp, read_only=True).active def norm(u): u = re.sub(r'https?://[^/]+', '', str(u or '')) u = u.split('?')[0] return u.rstrip('/') existing = set() pages = [] seen = set() for r in range(3, ws.max_row+1): k = ws.cell(r,11).value if k and 'hf.go.kr' in str(k): existing.add(norm(k)) n = norm(k) if '.do' in n and n not in seen: seen.add(n); pages.append(str(k).split('?')[0]) print(f"시트 hf페이지 {len(pages)}개, 기존URL {len(existing)}") TAB_JS = """ () => { const groups=[]; document.querySelectorAll('ul[class*=con0], ul.tab, ul[class*=tab], .tab-wrap ul, .sub-tab ul').forEach(ul=>{ const items=[...ul.querySelectorAll(':scope>li>a')].map(a=>({t:(a.textContent||'').trim().replace(/\\s+/g,' '), h:a.href})).filter(x=>x.t && /\\.do/.test(x.h) && !/#/.test(x.h)); if(items.length>=2){ const cls=String(ul.className); groups.push({cls, items}); } }); return groups; } """ found = {} with sync_playwright() as p: br=p.chromium.launch(headless=True) pg=br.new_context(ignore_https_errors=True,viewport={'width':1600,'height':1000}).new_page() for i,u in enumerate(pages): try: pg.goto(u, wait_until='domcontentloaded', timeout=15000); pg.wait_for_timeout(400) gs=pg.evaluate(TAB_JS) except Exception: gs=[] for g in gs: key=frozenset(norm(it['h']) for it in g['items']) if key not in found: found[key]=g['items'] if (i+1)%40==0: print(f" ...{i+1}/{len(pages)} (그룹 {len(found)})") br.close() print(f"\n탭 그룹 {len(found)}개:") missing_groups=[] for key,items in found.items(): miss=[it for it in items if norm(it['h']) not in existing] mark='✅전부있음' if not miss else f'⚠️누락 {len(miss)}' labs=', '.join(it['t'][:12] for it in items) print(f" [{mark}] {labs}") if miss: missing_groups.append({'items':items,'missing':miss}) json.dump(missing_groups, open(os.path.join(BASE,'_hf_missing_tabs.json'),'w',encoding='utf-8'), ensure_ascii=False, indent=1) print(f"\n누락 그룹 {len(missing_groups)}개 → _hf_missing_tabs.json")