# -*- coding: utf-8 -*- """공공기관 게시판 M(수량) 0 보정 — 총건수 추출 강화. 전략: ①총/전체/Total N건 정규식 ②목록행 글번호 최댓값(최신글=총건수) ③페이징 마지막페이지×페이지당행수. 대상: L==게시판 & M in (0,None,''). 못구하면 그대로(빈게시판 가능). 사용: python _공공기관_mfix.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) OUTDIR = r'D:\01.프로젝트\DB수집\공공기관' TOTAL = re.compile(r'(?:총|전체|total|건수)\s*[:\-]?\s*([\d,]+)\s*(?:건|개|page|페이지|item|EA)?', re.I) PAGE_PARAM = re.compile(r'(?:page|pageIndex|pageNo|cpage|curPage|nowPage|pageNum)\s*=\s*(\d+)', re.I) def from_total(txt): best = 0 for m in TOTAL.finditer(txt): d = m.group(1).replace(',', '') if d.isdigit(): best = max(best, int(d)) return best def from_rownums(body): """목록 행의 글번호(보통 첫 칸 숫자) 최댓값 = 최신글번호 ≈ 총건수.""" nums = [] rows = body.select('table tbody tr') or body.select('table tr') for tr in rows: cells = tr.find_all(['td', 'th']) if not cells: continue t = cells[0].get_text(strip=True).replace(',', '') if re.fullmatch(r'\d{1,7}', t): nums.append(int(t)) # ul형 게시판 if not nums: for li in body.select('ul li, ol li'): sp = li.find(['span', 'em', 'strong']) if sp: t = sp.get_text(strip=True).replace(',', '') if re.fullmatch(r'\d{1,7}', t): nums.append(int(t)) return max(nums) if nums else 0 def from_paging(body): pages = [] for a in body.select('.pagination a, .paging a, .page_nav a, .paginate a, nav a'): h = a.get('href', '') or '' m = PAGE_PARAM.search(h) if m: pages.append(int(m.group(1))) t = a.get_text(strip=True) if t.isdigit(): pages.append(int(t)) if not pages: return 0 last = max(pages) # 페이지당 행수 추정 rows = len(body.select('table tbody tr')) or len(body.select('.board_list li, .bbs_list li')) if rows and last: return last * rows # 근사(상한) return 0 def board_count(body): txt = body.get_text(' ', strip=True) c = from_total(txt) if c > 0: return c, '총건수' c = from_rownums(body) if c > 0: return c, '글번호max' c = from_paging(body) if c > 0: return c, '페이징근사' return 0, '없음' def run(name, sess): xlsx = os.path.join(OUTDIR, f'{name}.xlsx') wb = openpyxl.load_workbook(xlsx) ws = wb.active fixed = empty = 0 for r in range(3, ws.max_row + 1): if ws.cell(r, 2).value is None: break L = ws.cell(r, 12).value M = ws.cell(r, 13).value if L != '게시판' or (M not in (0, None, '')): continue url = ws.cell(r, 11).value if not url or not isinstance(url, str) or not url.startswith('http'): continue soup, _ = P.fetch(sess, url) if not soup: continue body = P.get_body(soup, P.BODY_SEL) c, how = board_count(body) if c > 0: ws.cell(r, 13).value = c; fixed += 1 else: # 빈 게시판 → N=없음, O=미부착 (글없으면 저작물없음) if ws.cell(r, 14).value: ws.cell(r, 14).value = '없음' empty += 1 wb.save(xlsx) print(f'[{name}] M보정 {fixed}행 | 빈게시판처리 {empty}행') return fixed, empty 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() tf = te = 0 for p in order: try: f, e = run(p['name'], sess); tf += f; te += e except Exception as ex: print(f"[{p['name']}] 실패: {ex}") print(f'=== M보정 합계 {tf}행 | 빈게시판 {te}행 ===') if __name__ == '__main__': main()