# -*- coding: utf-8 -*- """공공기관 L 재분류 — '페이지'로 오분류된 게시판 교정(eGov ESF board_pager/list/권호 위젯 누락 보강). 대상: L=='페이지' 행만 재검. 게시판이면 L=게시판·M산출. 기존 게시판/사이트 불변. 사용: python _공공기관_lfix.py [기관명 ...] """ import sys, os, re, json, importlib.util, warnings import openpyxl warnings.filterwarnings('ignore') spec = importlib.util.spec_from_file_location('p234', r'D:\01.프로젝트\DB수집\_스크립트\_공공기관_phase234.py') P = importlib.util.module_from_spec(spec); spec.loader.exec_module(P) specm = importlib.util.spec_from_file_location('mf', r'D:\01.프로젝트\DB수집\_스크립트\_공공기관_mfix.py') MF = importlib.util.module_from_spec(specm); specm.loader.exec_module(MF) OUTDIR = r'D:\01.프로젝트\DB수집\공공기관' LOG = open(r'D:\01.프로젝트\DB수집\_스크립트\_lfix_result.txt', 'w', encoding='utf-8') def say(s): LOG.write(s + '\n'); LOG.flush() def is_board(body): """강화 게시판 판별. 콘텐츠 스코프(get_body) 가정.""" # 1) 페이징 위젯(board_pager 포함) if body.select('[class*=pager], [class*=paging], [class*=pagination], [class*=paginate]'): # 단, 1페이지뿐인 빈 위젯 배제 위해 링크/번호 존재 확인은 생략(존재 자체가 게시판형) return True, '페이징위젯' # 2) 게시판 리스트 컨테이너 if body.select('[class*=board_list], [class*=bbs_list], [class*=gallery_list], [class*=photo_list], [class*=galleryList], table.bbs, [class*=board] table tbody tr'): return True, '리스트컨테이너' # 3) 발간물/정기간행물 권호 아카이브(select 또는 통권 다수) if body.select('select[name*=vol], select[name*=issue], select[id*=vol], [class*=volume], [class*=kwon], [class*=issue_list]'): return True, '권호아카이브' txt = body.get_text(' ', strip=True) if len(re.findall(r'통권\s*\d+\s*권', txt)) >= 3 or len(re.findall(r'제?\s*\d+\s*호', txt)) >= 4: return True, '권호다수' # 4) 총건수 텍스트 if P.TOTAL_PAT.search(txt) or re.search(r'(?:전체|총)\s*[:\-]?\s*\d[\d,]*\s*건', txt): return True, '총건수' # (list 링크다수 규칙은 부서소개 등 페이지 오탐이 많아 제외) return False, '' def run(name, sess): xlsx = os.path.join(OUTDIR, f'{name}.xlsx') wb = openpyxl.load_workbook(xlsx) ws = wb.active changed = 0 for r in range(3, ws.max_row + 1): if ws.cell(r, 2).value is None: break if ws.cell(r, 12).value != '페이지': continue url = ws.cell(r, 11).value if not url or not isinstance(url, str) or not url.startswith('http'): continue soup, final = P.fetch(sess, url) if not soup: continue body = P.get_body(soup, P.BODY_SEL) ok, why = is_board(body) if not ok: continue # 게시판으로 승격 + M 산출 c, how = MF.board_count(body) ws.cell(r, 12).value = '게시판' ws.cell(r, 13).value = c if c > 0 else 0 changed += 1 say(f' {name} r{r} 페이지→게시판 M={c}({how}) [{why}] {ws.cell(r,5).value or ws.cell(r,4).value}') wb.save(xlsx) say(f'[{name}] 페이지→게시판 {changed}행') return changed def main(): probe = {r['name']: r for r in json.load(open(r'D:\01.프로젝트\DB수집\_스크립트\_공공기관_probe.json', encoding='utf-8'))} order = sorted(probe.values(), key=lambda x: -int(x['num'])) only = sys.argv[1:] if only: order = [p for p in order if p['name'] in only or str(p['num']) in only] sess = P.make_session() tot = 0 for p in order: try: tot += run(p['name'], sess) except Exception as e: say(f"[{p['name']}] 실패: {e}") say(f'=== L재분류 합계 {tot}행 페이지→게시판 ===') LOG.close() if __name__ == '__main__': main()