# -*- coding: utf-8 -*- # 게시판 수량(M) Playwright 렌더 추출: python _cb_boardM.py <엑셀> # 렌더된 DOM에서 총게시물 카운트 추출, M 갱신. M>0인데 N=없음이면 어문으로 복구. import openpyxl, sys, io, re, time from playwright.sync_api import sync_playwright sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8') F=sys.argv[1] wb=openpyxl.load_workbook(F); ws=wb.active boards=[(r,ws.cell(r,11).value) for r in range(3,ws.max_row+1) if ws.cell(r,12).value=='게시판' and str(ws.cell(r,11).value or '').startswith('http')] print('게시판',len(boards),flush=True) JS=r'''()=>{ const t=document.body.innerText; let m=t.match(/(?:총\s*게시물|게시물|총게시글|전체)\s*[:\s]*([\d,]+)/); if(m) return parseInt(m[1].replace(/,/g,'')); m=t.match(/총\s*([\d,]+)\s*건/); if(m) return parseInt(m[1].replace(/,/g,'')); // fallback: 목록 tbody 행수 + 페이지수 추정 const tr=document.querySelectorAll('table tbody tr'); const pages=[...document.querySelectorAll('a')].map(a=>a.href||'').filter(h=>/page(Index|No|num)?=/i.test(h)); let maxp=1; pages.forEach(h=>{const mm=h.match(/page(?:Index|No|num)?=(\d+)/i); if(mm)maxp=Math.max(maxp,+mm[1]);}); if(tr.length) return (maxp>1?(maxp-1)*Math.max(tr.length,10):tr.length); return 0; }''' def nav(pg,u): for _ in range(2): try: pg.goto(u,wait_until='domcontentloaded'); pg.wait_for_timeout(900); return True except Exception: time.sleep(1) return False upd=0; fixed=0 with sync_playwright() as p: b=p.chromium.launch(); pg=b.new_page(); pg.set_default_timeout(15000) for i,(r,u) in enumerate(boards): M=0 try: if nav(pg,u): M=pg.evaluate(JS) or 0 except Exception: M=0 ws.cell(r,13).value=(M if M>0 else 0) upd+=1 if M>0 and ws.cell(r,14).value=='없음': ws.cell(r,14).value='어문'; fixed+=1 if i%25==0: print('%d/%d r%d M=%d'%(i+1,len(boards),r,M),flush=True) b.close() wb.save(F) from collections import Counter mm=[ws.cell(r,13).value for r,_ in boards] print('M갱신 %d | 없음복구 %d | M0게시판 %d'%(upd,fixed,sum(1 for x in mm if x in(0,None))))