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

87 lines
5.1 KiB
Python

# -*- coding: utf-8 -*-
# 국립해양생물자원관(21) N 재판정. 사용자기준: 사진=어문,이미지 / 조직도·인포그래픽(alt도식)=어문 / 빈게시판=없음.
# 학습 3~47 정확도 검증 + 48~끝 판정. 사용: _mabik_njudge.py [--apply]
import openpyxl, glob, os, re, sys, json, shutil, time
from playwright.sync_api import sync_playwright
from openpyxl.styles import PatternFill
BLUE=PatternFill(fill_type='solid',fgColor='FFBDD7EE')
APPLY='--apply' in sys.argv
BASE=os.path.dirname(os.path.abspath(__file__))
d=[x for x in glob.glob(os.path.join(BASE,'21.*')) if os.path.isdir(x)][0]
xp=[p for p in glob.glob(os.path.join(d,'*.xlsx')) if not os.path.basename(p).startswith(('_','~')) and 'conflict' not in os.path.basename(p) and 'backup' not in p][0]
SHOT=os.path.join(d,'_nshots'); os.makedirs(SHOT,exist_ok=True)
mt=os.path.getmtime(xp); wb=openpyxl.load_workbook(xp); ws=wb.active
UIPAT=re.compile(r'(logo|footer|header|banner|btn|icon|bg[_-]|[_-]bg|sns|family|bottom|top[_-]|arrow|blank|no[_-]?img|common|marker|sprite|loading|quick)',re.I)
DIAG=re.compile(r'(조직도|체계도|순서도|흐름도|구성도|개념도|자세한\s*내용은|텍스트로\s*보기|아래\s*표\s*참고)') # 조직도/인포그래픽=어문(사진·평면도·지도는 이미지로 유지)
# real-photo detector JS (scope #contens)
JS_PAGE=r'''()=>{
let root=document.querySelector("#contens")||document.querySelector("#txt")||document.querySelector("#contents")||document.body;
let imgs=[...root.querySelectorAll("img")].map(i=>({w:i.clientWidth,h:i.clientHeight,s:(i.currentSrc||i.src||"").split("/").pop(),alt:i.alt||""}));
let txt=(root.innerText||"").replace(/\s+/g," ").trim();
return {imgs:imgs, txtlen:txt.length, empty:/등록된\s*(게시물|자료)|게시물이\s*없|검색된\s*결과가\s*없/.test(txt)};
}'''
def realphotos(imgs):
out=[]
for im in imgs:
if im['w']<140 or im['h']<100: continue
if UIPAT.search(im['s']): continue
if DIAG.search(im['alt'] or ''): continue # 접근성 도식=어문 취급
out.append(im)
return out
rows=[]
for r in range(3,ws.max_row+1):
if ws.cell(r,2).value is None: continue
L=ws.cell(r,12).value
if L not in ('페이지','게시판'): continue
rows.append({'r':r,'L':L,'M':ws.cell(r,13).value,'N':str(ws.cell(r,14).value or ''),'K':str(ws.cell(r,11).value or ''),
'lab':ws.cell(r,7).value or ws.cell(r,6).value or ws.cell(r,5).value or ''})
def judge(pg,row):
u=row['K']
try:
pg.goto(u,timeout=30000,wait_until='domcontentloaded'); pg.wait_for_timeout(1000); pg.mouse.wheel(0,2500); pg.wait_for_timeout(500)
except Exception as e: return ('?','ERR '+str(e)[:30],[])
info=pg.evaluate(JS_PAGE)
if row['L']=='페이지':
photos=realphotos(info['imgs'])
return ('어문,이미지' if photos else '어문', 'img%d'%len(photos), [p['s'] for p in photos[:4]])
# 게시판: 빈게시판=없음 / 목록에 콘텐츠 사진썸네일(갤러리)=어문,이미지 / 그외 텍스트목록=어문
# (48~끝엔 교육프로그램 포스터보드 없음→list썸네일 신뢰가능)
if row['M'] in (0,'0') or info['empty']:
return ('없음','빈게시판',[])
photos=realphotos(info['imgs'])
if len(photos)>=2:
return ('어문,이미지','목록썸네일%d'%len(photos),[p['s'] for p in photos[:4]])
return ('어문','텍스트목록',[])
results=[]
with sync_playwright() as pw:
br=pw.chromium.launch(); pg=br.new_page(viewport={'width':1200,'height':1600})
for row in rows:
pred,why,ev=judge(pg,row)
results.append({**row,'pred':pred,'why':why,'ev':ev})
# 몽타주: 48~끝만 저장(검증용)
if row['r']>=48:
try:
el=pg.query_selector('#contens') or pg.query_selector('#txt')
(el or pg).screenshot(path=os.path.join(SHOT,'r%d_%s.png'%(row['r'],pred.replace(',','_'))))
except Exception: pass
br.close()
# 학습 정확도(3~47)
learn=[x for x in results if x['r']<48]
ok=sum(1 for x in learn if x['pred']==x['N'])
print('학습 3~47 정확도 %d/%d'%(ok,len(learn)))
for x in learn:
if x['pred']!=x['N']: print(' XX r%d %s N=%s pred=%s (%s) %s'%(x['r'],x['lab'],x['N'],x['pred'],x['why'],x['ev'][:2]))
# 48~끝 변경
tgt=[x for x in results if x['r']>=48]
chg=[x for x in tgt if x['pred']!=x['N']]
print('48~끝 대상 %d행, 변경 %d'%(len(tgt),len(chg)))
for x in chg: print(' r%d %s : %s -> %s (%s) %s'%(x['r'],x['lab'],x['N'],x['pred'],x['why'],x['ev'][:2]))
json.dump(results,open(os.path.join(BASE,'_mabik_nresults.json'),'w',encoding='utf-8'),ensure_ascii=False,indent=0)
if not APPLY: print('\nDRY. --apply 로 48~끝 N 반영(몽타주 _nshots\\).'); sys.exit(0)
for x in tgt:
if x['pred']!='?' and x['pred']!=x['N']:
ws.cell(x['r'],14).value=x['pred']; ws.cell(x['r'],14).fill=BLUE
if os.path.getmtime(xp)!=mt: print('ABORT changed'); sys.exit(1)
shutil.copy(xp, os.path.join(d,'_backup',os.path.basename(xp).replace('.xlsx','_backup_N재판정전.xlsx')))
wb.save(xp); print('saved. 48~끝 N 반영 %d행 변경'%len(chg))