# -*- coding: utf-8 -*- # 국악방송(27) radio_program_board.jsp 행 L/M/N 재판별. # L: 진행자소개&총수<=1→페이지M1 / 총수0→게시판M0(없음) / 그외→게시판M=p.total # N: 상세 본문 실사진(목록 배너 제외=상세전용 img, size>=140x100, UI패턴 제외) 있으면 어문,이미지 / 없으면 어문 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 cj=http.cookiejar.CookieJar(); op=urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx),urllib.request.HTTPCookieProcessor(cj)) op.addheaders=[('User-Agent','Mozilla/5.0')] BLUE=PatternFill(fill_type='solid',fgColor='FFBDD7EE'); APPLY='--apply' in sys.argv UIPAT=re.compile(r'(logo|footer|header|banner|btn|icon|bg[_-]|[_-]bg|sns|family|arrow|blank|no[_-]?img|sprite|marker|common|onair|chn_|ico_|_kt|_sk|_lg)',re.I) def fixdom(u): return str(u).replace('https://new.igbf.kr','http://www.igbf.kr').replace('http://new.igbf.kr','http://www.igbf.kr') def gf(u): try: return op.open(fixdom(u),timeout=20).read().decode('utf-8','replace') except: return '' TOT=re.compile(r'class=\"total\"[^>]*>.*?\((\d+)\)',re.S) 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] 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 rows=[r for r in range(3,ws.max_row+1) if ws.cell(r,2).value is not None and 'radio_program_board.jsp' in str(ws.cell(r,11).value or '')] print('radio_program_board 행 %d개'%len(rows)) JS=r'''()=>[...document.querySelectorAll("img")].filter(i=>i.clientWidth>=140&&i.clientHeight>=100).map(i=>(i.currentSrc||i.src||"").split("/").pop())''' plan={} with sync_playwright() as pw: br=pw.chromium.launch(); pg=br.new_page(viewport={'width':1280,'height':1700}) for r in rows: u=str(ws.cell(r,11).value or ''); lab=str(ws.cell(r,6).value or '') h=gf(u); m=TOT.search(h); tot=int(m.group(1)) if m else None # L/M if tot==0: L,M,Nbase=('게시판',0,'없음') elif re.search(r'진행자|소개',lab) and (tot is None or tot<=1): L,M,Nbase=('페이지',1,None) else: L,M,Nbase=('게시판', tot if tot is not None else 0, None) # N (없음이면 skip) Nv='없음' if Nbase=='없음' else '어문' if Nbase!='없음': try: pg.goto(fixdom(u),timeout=25000,wait_until='domcontentloaded'); pg.wait_for_timeout(700); pg.mouse.wheel(0,1200); pg.wait_for_timeout(250) base=set(x for x in pg.evaluate(JS) if not UIPAT.search(x)) lh=pg.content(); bids=list(dict.fromkeys(re.findall(r'bIdx=(\d+)',lh)))[:3] found=False for b in bids: pg.goto(fixdom(u)+'&state=view&bIdx='+b,timeout=25000,wait_until='domcontentloaded'); pg.wait_for_timeout(600); pg.mouse.wheel(0,1500); pg.wait_for_timeout(250) det=[x for x in pg.evaluate(JS) if not UIPAT.search(x)] extra=[x for x in det if x not in base] if extra: found=True; break Nv='어문,이미지' if found else '어문' except Exception: Nv='어문' plan[r]=(L,M,Nv,lab) time.sleep(0.02) print('판정 완료. 변경 미리보기:') chg=0 for r in rows: L,M,Nv,lab=plan[r] cur=(ws.cell(r,12).value,ws.cell(r,13).value,str(ws.cell(r,14).value or '')) if (L,M,Nv)!=cur: chg+=1 if chg<=40: print(' r%d [%s] %s -> L=%s M=%s N=%s'%(r,lab,cur,L,M,Nv)) print('변경 %d행'%chg) import json json.dump({str(r):plan[r] for r in rows},open(os.path.join(DIR,'_gugak_radio_lmn.json'),'w',encoding='utf-8'),ensure_ascii=False) if not APPLY: print('DRY'); sys.exit(0) mt=os.path.getmtime(xp); wb=openpyxl.load_workbook(xp); ws=wb.active n=0 for r in rows: if ws.cell(r,2).value is None: continue L,M,Nv,lab=plan[r] if ws.cell(r,12).value!=L: ws.cell(r,12).value=L; ws.cell(r,12).fill=BLUE; n+=1 if ws.cell(r,13).value!=M: ws.cell(r,13).value=M; ws.cell(r,13).fill=BLUE if str(ws.cell(r,14).value or '')!=Nv: ws.cell(r,14).value=Nv; ws.cell(r,14).fill=BLUE try: if os.path.getmtime(xp)==mt: shutil.copy(xp, os.path.join(d,'_backup',os.path.basename(xp).replace('.xlsx','_backup_radioLMN전.xlsx'))); wb.save(xp); print('saved 변경셀반영') else: print('ABORT(변경됨)') except PermissionError: print('LOCKED')