공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
92 lines
3.1 KiB
Python
92 lines
3.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""공주시 전용: 본문에 인페이지 앵커 탭(ul.tab-ul 안 a[href^="#"], 예: #nav1~4)이 있는
|
|
페이지는 1행 유지하되 수량(M, 13열)을 그 탭 개수로 기입.
|
|
|
|
판별: class 에 'tab-ul' 포함한 ul 안에서 href 가 '#' 로 시작하는 탭 <a> 개수(>=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()
|