DB_JOB/_스크립트/_zz_emptyboard.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

77 lines
4.1 KiB
Python

# -*- coding: utf-8 -*-
"""억제된 페이지→게시판 후보(아직 페이지인 행)를 빈게시판 신호로 재분석. 읽기전용."""
import sys, os, re, json, importlib.util, time
from concurrent.futures import ThreadPoolExecutor, as_completed
import openpyxl
HERE = os.path.dirname(os.path.abspath(__file__)); TEMP = os.path.join(HERE,'..','_temp')
MODULES = ['_chungnam_phase234_all.py','_chungbuk_phase234_all.py','_jeonbuk_phase234_all.py']
def load():
sites,mods={},{}
for f in MODULES:
sp=importlib.util.spec_from_file_location(f[:-3],os.path.join(HERE,f));m=importlib.util.module_from_spec(sp);sp.loader.exec_module(m)
for k,v in m.SITES.items(): sites[k]=v;mods[k]=m
return sites,mods
EMPTY=re.compile(r'게시물이?\s*없|등록된?\s*(?:게시물|자료|글|내용)\s*가?\s*없|자료가\s*없|검색된\s*(?:게시물|결과)\s*가?\s*없|데이터가\s*없')
BOARDDOM='.board_list,.bbs_list,table.board_list,.board_view,.board_wrap,.bbs,.tbl_list,.board,.list_wrap,.gallery_list,.photo_list,table.board'
WRITE=re.compile(r'글쓰기|글등록|등록하기|write|글작성')
def mkfetch(M,cfg):
if hasattr(M,'make_session'):
s=M.make_session(weak_ssl=cfg.get('weak_ssl',False)); return lambda u:M.fetch(s,u)
return lambda u:M.fetch(u)
def analyze(M,url,bsel,fetch):
soup=None
for k in range(3):
soup=fetch(url)
if soup is not None: break
time.sleep(0.4*(k+1))
if soup is None: return {'v':'fail'}
b=M.get_body(soup,bsel)
txt=b.get_text(' ',strip=True)
search=len([i for i in b.find_all('input') if (i.get('type') or 'text').lower() in ('text','search')])>0
rows=[el for el in b.select('table tbody tr, .board_list li, ul.bbs_list li, .bbs_list li') if el.find('a',href=True)]
empty=bool(EMPTY.search(txt))
boarddom=bool(b.select(BOARDDOM))
paging=bool(b.select('.pagination,.paging,nav.paging,.page_nav,.paginate'))
write=bool(WRITE.search(txt))
if empty or (boarddom and (paging or write)):
v='empty_board'
else:
v='page'
return {'v':v,'search':search,'rows':len(rows),'empty':empty,'boarddom':boarddom,'paging':paging,'write':write,
'snip':txt[:160]}
def main():
sites,mods=load(); names=[a for a in sys.argv[1:] if not a.startswith('--')]
for n in names:
cfg=sites[n];M=mods[n];fetch=mkfetch(M,cfg)
wb=openpyxl.load_workbook(cfg['xlsx'],read_only=True);ws=wb.active
d=json.load(open(os.path.join(TEMP,f'_audit_{n}.json'),encoding='utf-8'))
cands=[]
for x in d['L_diffs']:
if x['oldL']=='페이지' and x['newL']=='게시판':
if ws.cell(x['r'],12).value=='페이지': # 아직 페이지(억제됨)
cands.append(x)
wb.close()
res=[]
with ThreadPoolExecutor(max_workers=6) as ex:
futs={ex.submit(analyze,M,x['url'],cfg['body_sel'],fetch):x for x in cands}
for f in as_completed(futs):
x=futs[f]
try:r=f.result()
except:r={'v':'fail'}
res.append((x,r))
eb=[(x,r) for x,r in res if r['v']=='empty_board']
pg=[(x,r) for x,r in res if r['v']=='page']
nf=len([1 for _,r in res if r['v']=='fail'])
print('\n==== %s: 억제후보%d → 빈게시판의심%d 페이지%d 실패%d'%(n,len(cands),len(eb),len(pg),nf))
print('--- 빈게시판 의심(empty_board) 예시 ---')
for x,r in sorted(eb,key=lambda t:t[0]['r'])[:8]:
print(f" r{x['r']} {(x['label'] or '')[:20]} | empty={r['empty']} boarddom={r['boarddom']} paging={r['paging']} | {x['url'][:62]}")
print(f" · {r['snip'][:110]}")
print('--- 순수 페이지 예시 ---')
for x,r in sorted(pg,key=lambda t:t[0]['r'])[:5]:
print(f" r{x['r']} {(x['label'] or '')[:20]} | boarddom={r['boarddom']} | {x['url'][:62]}")
print(f" · {r['snip'][:110]}")
json.dump([{**x,**r} for x,r in res],open(os.path.join(TEMP,f'_emptyboard_{n}.json'),'w',encoding='utf-8'),ensure_ascii=False,indent=1)
if __name__=='__main__': main()