# -*- coding: utf-8 -*- """공주시 전용: 본문에 인페이지 앵커 탭(ul.tab-ul 안 a[href^="#"], 예: #nav1~4)이 있는 페이지는 1행 유지하되 수량(M, 13열)을 그 탭 개수로 기입. 판별: class 에 'tab-ul' 포함한 ul 안에서 href 가 '#' 로 시작하는 탭 개수(>=2). 한 페이지에 그런 탭그룹이 여럿이면 가장 큰 그룹의 탭 수. 사용: python -X utf8 _gongju_navcount.py [--write] """ import sys, warnings from concurrent.futures import ThreadPoolExecutor, as_completed import openpyxl, requests, shutil from bs4 import BeautifulSoup warnings.filterwarnings('ignore') XLSX = r'D:\01.프로젝트\DB수집\작업파일\광역_사이트맵\충청남도\2.공주시\충청남도_공주시.xlsx' DOMAIN = 'gongju.go.kr' H = {'User-Agent': 'Mozilla/5.0 Chrome/120 Safari/537.36'} def nav_count(html): soup = BeautifulSoup(html, 'html.parser') best = 0 for ul in soup.find_all('ul'): cls = ' '.join(ul.get('class') or []).lower() if 'tab-ul' not in cls: continue anchors = [a for a in ul.find_all('a') if (a.get('href') or '').strip().startswith('#') and a.get_text(strip=True)] if len(anchors) >= 2: best = max(best, len(anchors)) return best def fetch(r, u): try: x = requests.get(u, headers=H, timeout=15, verify=False) return r, u, x.content except Exception: return r, u, None def main(): write = '--write' in sys.argv wb = openpyxl.load_workbook(XLSX) ws = wb.active targets = [] for r in range(3, ws.max_row + 1): u = ws.cell(r, 11).value if isinstance(u, str) and DOMAIN in u: targets.append((r, u)) print(f'스캔 대상(동일도메인): {len(targets)}행') res = {} with ThreadPoolExecutor(max_workers=8) as ex: for f in as_completed([ex.submit(fetch, r, u) for r, u in targets]): r, u, html = f.result() if html: c = nav_count(html) if c >= 2: res[r] = (u, c) print(f'\n인페이지 탭(#) 보유 페이지: {len(res)}건') print(f"{'행':>4} {'현L':<5}{'현M':>4} → {'새M':>4} F/카테고리 | URL") print('-' * 90) chg = 0 for r in sorted(res): u, c = res[r] L = ws.cell(r, 12).value or '' M = ws.cell(r, 13).value cat = ws.cell(r, 6).value or ws.cell(r, 5).value or ws.cell(r, 4).value or '' mark = '' if str(M) == str(c) else '★' if str(M) != str(c): chg += 1 print(f"{r:>4} {str(L):<5}{str(M):>4} → {c:>4}{mark} {cat} | {u}") if write: ws.cell(r, 13).value = c print('-' * 90) print(f'변경 대상: {chg}행') if write and chg: bak = XLSX.replace('.xlsx', '_backup_navcount전.xlsx') shutil.copy(XLSX, bak) wb.save(XLSX) print(f'저장 완료. 백업: {bak}') elif not write: print('(DRY — 실제 기입하려면 --write)') if __name__ == '__main__': main()