DB_JOB/_김제_인페이지탭수.py
hehihoho3 df16c98366 백업: DB수집 전체 스냅샷 (공공기관2 정리 전)
공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 18:15:40 +09:00

117 lines
4.5 KiB
Python

# -*- coding: utf-8 -*-
"""김제시 인페이지 탭(#anchor) 수량 → M 기입.
페이지 본문의 div[class*=basic_tab] 중 탭 링크가 전부 '#anchor'인 그룹(예 basic_tab2>ul.col4,
#tab1~#tabN)을 인페이지 스크롤탭으로 보고 M=탭수 기입(공주/금산 전례, 매뉴얼 1-5b: #anchor는 행 분리 안함).
menuCd 형제 nav(basic_tab depth4 등, href=실제 .gimje)는 제외. L은 페이지 유지.
대상: 17행~끝(요청). 3~16행은 참고 보고만.
사용: python -X utf8 _김제_인페이지탭수.py dry | run
"""
import os, sys, re, io, time, shutil
from concurrent.futures import ThreadPoolExecutor, as_completed
import warnings; warnings.filterwarnings('ignore')
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', line_buffering=True)
import requests
from bs4 import BeautifulSoup
import openpyxl
HERE = os.path.dirname(os.path.abspath(__file__))
XP = os.path.join(HERE, '작업파일', '광역_사이트맵', '전북특별자치도', '3.김제시', '전북특별자치도_김제시.xlsx')
UA = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120 Safari/537.36'}
def fetch(session, url):
r = session.get(url, headers=UA, verify=False, timeout=10, allow_redirects=True)
meta = re.search(rb'charset=["\']?\s*([\w-]+)', r.content[:3000], re.I)
r.encoding = meta.group(1).decode('ascii', 'ignore') if meta else r.apparent_encoding
return BeautifulSoup(r.text, 'html.parser')
BASIC_TAB = re.compile('basic_tab')
def inpage_tabs(soup):
"""가장 큰 '전부 #anchor' basic_tab 그룹의 (탭수, 라벨)."""
best, labels = 0, None
for d in soup.find_all('div', class_=BASIC_TAB):
ul = d.find('ul')
if not ul:
continue
links = ul.select('li > a')
if len(links) < 2:
continue
hrefs = [(a.get('href') or '').strip() for a in links]
if all(h.startswith('#') for h in hrefs):
if len(links) > best:
best = len(links)
labels = [a.get_text(strip=True) for a in links]
return best, labels
def main():
mode = sys.argv[1] if len(sys.argv) > 1 else 'dry'
wb = openpyxl.load_workbook(XP); ws = wb.active
last = max(r for r in range(3, ws.max_row + 1) if ws.cell(r, 2).value not in (None, ''))
def lab(r):
return ' > '.join(str(ws.cell(r, c).value) for c in range(4, 11) if ws.cell(r, c).value not in (None, ''))
# 대상 수집: L=페이지 & http URL
def targets(lo, hi):
out = []
for r in range(lo, hi + 1):
L = ws.cell(r, 12).value
url = ws.cell(r, 11).value
if L == '페이지' and isinstance(url, str) and url.startswith('http'):
out.append((r, url))
return out
main_t = targets(17, last)
pre_t = targets(3, 16)
session = requests.Session()
def scan(rows_urls):
res = {}
def w(ru):
r, u = ru
try:
n, ls = inpage_tabs(fetch(session, u))
return r, n, ls
except Exception:
return r, 0, None
with ThreadPoolExecutor(max_workers=10) as ex:
for f in as_completed([ex.submit(w, ru) for ru in rows_urls]):
r, n, ls = f.result()
if n >= 2:
res[r] = (n, ls)
return res
print(f'스캔: 17~{last} ({len(main_t)}개 페이지), 참고 3~16 ({len(pre_t)}개)')
t0 = time.time()
main_hits = scan(main_t)
pre_hits = scan(pre_t)
print(f'스캔완료 {time.time()-t0:.0f}s')
print(f'\n[대상 17~끝] 인페이지탭 발견 {len(main_hits)}행:')
for r in sorted(main_hits):
n, ls = main_hits[r]
old = ws.cell(r, 13).value
print(f' r{r}: M {old}{n} [{lab(r)}] 탭={ls}')
if pre_hits:
print(f'\n[참고 3~16] 인페이지탭 {len(pre_hits)}행 (요청범위 밖, 미적용):')
for r in sorted(pre_hits):
n, ls = pre_hits[r]
print(f' r{r}: M {ws.cell(r,13).value}{n}? [{lab(r)}] 탭={ls}')
if mode != 'run':
return
bak = XP.replace('.xlsx', '_backup_인페이지탭전.xlsx')
shutil.copy(XP, bak)
for r, (n, ls) in main_hits.items():
ws.cell(r, 13).value = n
try:
wb.save(XP); print(f'\n저장완료. {len(main_hits)}행 M 갱신. 백업 {os.path.basename(bak)}')
except PermissionError:
wb.save(XP.replace('.xlsx', '_LP.xlsx')); print('\n!! 잠김 → _LP')
if __name__ == '__main__':
main()