# -*- coding: utf-8 -*- # 국악방송(27) N 재작성 엑셀32~끝 + 악기 인페이지탭 M=탭수. # 3~31 사용자검수 N을 라벨→N 학습 + 도메인 휴리스틱. mtime가드·백업. 사용: [--apply] import openpyxl, glob, os, re, sys, shutil, time, urllib.request, ssl, http.cookiejar from openpyxl.styles import PatternFill from playwright.sync_api import sync_playwright ctx=ssl.create_default_context(); ctx.check_hostname=False; ctx.verify_mode=ssl.CERT_NONE BLUE=PatternFill(fill_type='solid',fgColor='FFBDD7EE'); APPLY='--apply' in sys.argv DIR=os.path.dirname(os.path.abspath(__file__)) d=[x for x in glob.glob(os.path.join(DIR,'27.*')) 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] mt=os.path.getmtime(xp); wb=openpyxl.load_workbook(xp); ws=wb.active def eff(r,c): v=ws.cell(r,c).value if v is not None: return v for mr in ws.merged_cells.ranges: if mr.min_col==c and mr.min_row<=r<=mr.max_row: return ws.cell(mr.min_row,c).value return None data=[r for r in range(3,ws.max_row+1) if ws.cell(r,2).value is not None] def label(r): return str(ws.cell(r,6).value or eff(r,5) or '') # 1) 3~31 학습: 라벨->N (최빈) from collections import Counter,defaultdict learn=defaultdict(Counter) for r in data: if r<=31: learn[label(r)][str(ws.cell(r,14).value or '')]+=1 learned={k:v.most_common(1)[0][0] for k,v in learn.items()} ORDER={'어문':0,'이미지':1,'영상':2,'오디오':3,'글꼴':4,'3D':5,'기타':6} def compose(parts): parts=sorted(set(['어문']+parts),key=lambda x:ORDER.get(x,9)) return ','.join(parts) # 2) 휴리스틱 (라벨 기반) def heuristic(r): lab=label(r); E=str(eff(r,5) or ''); D=str(eff(r,4) or ''); L=ws.cell(r,12).value; M=ws.cell(r,13).value if (M in (0,'0')) and L=='게시판': return '없음' # 악기(우리악기 톺아보기 하위, 소개 제외)=탭 인터뷰/연주법/제작과정/음원 if E=='우리악기 톺아보기' and lab not in ('소개',): return compose(['이미지','영상','오디오']) # 오디오 아카이브 if re.search(r'(음원|반주음악|음반|새음원|교육자료|국악\s*교육)',lab): return compose(['오디오']) if re.search(r'(명인명창|구술)',lab): return compose(['영상']) # 영상 if re.search(r'(방송보기|다시보기|동영상|영상|보이는\s*라디오|VOD)',lab): return compose(['영상']) # 오디오 프로그램 if re.search(r'(특집\s*프로그램)',lab): return compose(['오디오']) # 사진/이미지 if re.search(r'(사진|갤러리|포토)',lab): return compose(['이미지']) # 소개/인사말/연혁/CI/비전/조직/채널현황 = 인트로(이미지) if re.search(r'(소개|인사말|연혁|CI|비전|조직|채널현황|프로그램\s*소개|출연자)',lab): return compose(['이미지']) # 게시판 텍스트류 / 기타 = 어문 return compose([]) # 3) 악기 M=탭수 (ul.tabtn) 크롤 inst_rows=[r for r in data if eff(r,5)=='우리악기 톺아보기' and label(r)!='소개'] tabcount={} def fixdom(u): return str(u).replace('https://new.igbf.kr','http://www.igbf.kr') with sync_playwright() as pw: br=pw.chromium.launch(); pg=br.new_page() for r in inst_rows: try: pg.goto(fixdom(ws.cell(r,11).value),timeout=25000,wait_until='domcontentloaded'); pg.wait_for_timeout(900) tabcount[r]=len(pg.query_selector_all('ul.tabtn li a')) except Exception: tabcount[r]=None br.close() print('악기 탭수:',{label(r):tabcount[r] for r in inst_rows}) # 4) 판정: 32~끝 changes=[]; mchanges=[] for r in data: if r<32: continue lab=label(r); cur=str(ws.cell(r,14).value or '') pred= learned.get(lab) if lab in learned else heuristic(r) # 우리악기 하위는 항상 휴리스틱(학습라벨 '소개' 충돌 방지) if eff(r,5)=='우리악기 톺아보기': pred=heuristic(r) if (ws.cell(r,13).value in (0,'0')) and ws.cell(r,12).value=='게시판': pred='없음' if pred and pred!=cur: changes.append((r,lab,cur,pred)) # 악기 M=탭수 if r in tabcount and tabcount[r] and ws.cell(r,13).value!=tabcount[r]: mchanges.append((r,lab,ws.cell(r,13).value,tabcount[r])) print('학습 라벨수 %d'%len(learned)) print('N 변경 %d행:'%len(changes)) for r,lab,cur,pred in changes: print(' r%d [%s] %s -> %s'%(r,lab,cur,pred)) print('악기 M(탭수) 변경 %d행:'%len(mchanges), [(r,l,o,n) for r,l,o,n in mchanges]) if not APPLY: print('DRY'); sys.exit(0) for r,lab,cur,pred in changes: ws.cell(r,14).value=pred; ws.cell(r,14).fill=BLUE for r,lab,o,nv in mchanges: ws.cell(r,13).value=nv; ws.cell(r,13).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 N변경%d M변경%d'%(len(changes),len(mchanges)))