공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
86 lines
4.9 KiB
Python
86 lines
4.9 KiB
Python
# -*- 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')
|