# -*- coding: utf-8 -*-
"""공주시: 인페이지(#nav) 탭을 가진 페이지의 각 탭 패널 안에 '게시판'이 있는지 점검.

대상: _gongju_navcount 에서 잡힌 14개 페이지(동일도메인 자동 재탐지).
각 탭 <a href="#navN"> → 패널 element(id=navN) 내부에서 게시판 신호 탐지:
  · 목록 table(td 안 링크 다수) · 페이징 · '총 N건' · 상세링크(view.do/mode=V/nttId/BBSMSTR) · iframe
판정: 신호 1+ → 그 탭은 '게시판 포함' 가능.

사용: python -X utf8 _gongju_navtab_board.py
"""
import re, warnings
from concurrent.futures import ThreadPoolExecutor, as_completed
import openpyxl, requests
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'}
DETAIL = re.compile(r'(view\.do|mode=V|nttId|BBSMSTR|selectBoard|selectBbs)', re.I)
TOTAL = re.compile(r'총\s*[\d,]+\s*(건|개)')


def get_navtabs(soup):
    """type3(#nav) 탭그룹의 [(label, panel_id)] 반환 (가장 큰 그룹)."""
    best = []
    for ul in soup.find_all('ul'):
        cls = ' '.join(ul.get('class') or []).lower()
        if 'tab-ul' not in cls:
            continue
        tabs = []
        for a in ul.find_all('a'):
            h = (a.get('href') or '').strip()
            t = a.get_text(strip=True)
            if h.startswith('#') and len(h) > 1 and t:
                tabs.append((t, h[1:]))
        if len(tabs) >= 2 and len(tabs) > len(best):
            best = tabs
    return best


def board_signals(panel):
    if panel is None:
        return []
    sig = []
    # 목록 테이블: 행 3+ 이고 링크 포함
    for tb in panel.find_all('table'):
        rows = tb.find_all('tr')
        links = tb.find_all('a', href=True)
        if len(rows) >= 3 and len(links) >= 3:
            sig.append('목록table')
            break
    if panel.select('.paging,.pagination,.board_paging,.bbs_paging'):
        sig.append('페이징')
    if TOTAL.search(panel.get_text(' ', strip=True)):
        sig.append('총건수')
    if any(DETAIL.search(a['href']) for a in panel.find_all('a', href=True)):
        sig.append('상세링크')
    if panel.find('iframe'):
        sig.append('iframe')
    return sig


def fetch(r, u):
    try:
        return r, u, requests.get(u, headers=H, timeout=15, verify=False).content
    except Exception:
        return r, u, None


def main():
    ws = openpyxl.load_workbook(XLSX).active
    targets = [(r, ws.cell(r, 11).value) for r in range(3, ws.max_row + 1)
               if isinstance(ws.cell(r, 11).value, str) and DOMAIN in ws.cell(r, 11).value]
    htmls = {}
    with ThreadPoolExecutor(max_workers=8) as ex:
        for f in as_completed([ex.submit(fetch, r, u) for r, u in targets]):
            r, u, h = f.result()
            if h:
                htmls[r] = (u, h)

    found_pages = 0
    board_hits = 0
    for r in sorted(htmls):
        u, h = htmls[r]
        soup = BeautifulSoup(h, 'html.parser')
        tabs = get_navtabs(soup)
        if len(tabs) < 2:
            continue
        found_pages += 1
        cat = ws.cell(r, 6).value or ws.cell(r, 5).value or ws.cell(r, 4).value or ''
        per = []
        for label, pid in tabs:
            sig = board_signals(soup.find(id=pid))
            per.append((label, sig))
        any_board = any(s for _, s in per)
        flag = '🟢게시판 발견' if any_board else '— 게시판 없음(정적 콘텐츠)'
        print(f'\n[행{r}] {cat}  탭{len(tabs)}개  {flag}')
        print(f'      {u}')
        for label, sig in per:
            mk = ('게시판? ' + ','.join(sig)) if sig else '정적'
            print(f'        · {label[:30]:<30} {mk}')
            if sig:
                board_hits += 1
    print(f'\n===== 요약: {found_pages}개 페이지 / 게시판 신호 탭 {board_hits}개 =====')


if __name__ == '__main__':
    main()
