DB_JOB/작업파일/완료/광역_사이트맵/충청북도/_cy4_board.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

48 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
# 게시판 후보 M(글수) 추출 + 보드확정(리스트행/날짜). 서브사이트홈 false-board 강등. 공공누리 푸터 재확인.
import json, io, sys, re, time
from playwright.sync_api import sync_playwright
sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8')
d=json.load(open('_cy4.json',encoding='utf-8'))
boards={rs:v for rs,v in d.items() if v.get('L')=='게시판'}
print('게시판 후보',len(boards),flush=True)
JS=r'''()=>{
const t=document.body?document.body.innerText:'';
const tot=(t.match(/총\s*게시물\s*[:]?\s*([\d,]+)/)||t.match(/총\s*([\d,]+)\s*건/)||t.match(/게시물\s*수\s*[:]?\s*([\d,]+)/)||t.match(/Total\s*[:]?\s*([\d,]+)/i)||t.match(/전체\s*[:]?\s*([\d,]+)/));
// 리스트 행: 번호+날짜 패턴
let rowsWithDate=0, maxno=0;
const main=document.querySelector('#contents,.contents,.sub_contents,#container,main')||document.body;
main.querySelectorAll('table tbody tr, ul li, .board_list li').forEach(tr=>{
const tx=tr.innerText||'';
if(/20\d\d[-.\/]\s?\d\d?[-.\/]\s?\d\d?/.test(tx)){ rowsWithDate++; const m=tx.match(/^\s*(\d{1,6})/); if(m){const n=parseInt(m[1]); if(n>maxno)maxno=n;} }
});
// 공공누리 푸터/본문
function kogl(root){const imgs=[...root.querySelectorAll('img')].filter(im=>/opentype0?(\d)/i.test(im.getAttribute('src')||''));const ty=new Set();let link=false;for(const im of imgs){const m=/opentype0?(\d)/i.exec(im.getAttribute('src')||'');if(m)ty.add(parseInt(m[1]));for(let n=im;n;n=n.parentElement){if(n.tagName==='A'&&n.getAttribute('href')&&n.getAttribute('href')!=='#'){link=true;break;}}}return{types:[...ty].sort(),link};}
const k=kogl(main);
const av=[...main.querySelectorAll('a')].filter(a=>/selectBoardArticle|nttId=|articleNo=|artclView/i.test(a.href||'')).length;
return {tot:tot?parseInt(tot[1].replace(/,/g,'')):null, rowsWithDate, maxno, av, kogl:k};
}'''
def nav(pg,u):
for _ in range(2):
try: pg.goto(u,wait_until='domcontentloaded',timeout=22000); pg.wait_for_timeout(1500); return True
except Exception: time.sleep(1)
return False
out={}
with sync_playwright() as p:
b=p.chromium.launch(); pg=b.new_page(viewport={'width':1300,'height':2600}); pg.set_default_timeout(22000)
for rs,v in sorted(boards.items(),key=lambda x:int(x[0])):
u=v['u']; r={'lab':v['lab']}
try:
if nav(pg,u):
e=pg.evaluate(JS)
M = e['tot'] if e['tot'] is not None else (e['maxno'] if e['maxno']>0 else (e['rowsWithDate'] if e['rowsWithDate']>0 else 0))
isboard = e['rowsWithDate']>=1 or e['av']>=1 or (e['tot'] is not None)
r.update(M=M, isboard=isboard, rwd=e['rowsWithDate'], av=e['av'], tot=e['tot'], maxno=e['maxno'], kogl=e['kogl'])
else: r.update(M=0,isboard=True,err='nav')
except Exception as ex: r.update(M=0,isboard=True,err=str(ex)[:30])
out[rs]=r
flag='' if r.get('isboard') else ' <<<NOT-BOARD(페이지강등)'
print('r%s %-18s M=%s rwd=%s av=%s tot=%s kogl=%s%s'%(rs,v['lab'][:18],r.get('M'),r.get('rwd'),r.get('av'),r.get('tot'),r.get('kogl',{}).get('types'),flag),flush=True)
b.close()
json.dump(out,open('_cy4_board.json','w',encoding='utf-8'),ensure_ascii=False)