공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
77 lines
2.7 KiB
Python
77 lines
2.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
import requests, urllib3, re, sys, io
|
|
from bs4 import BeautifulSoup
|
|
import openpyxl
|
|
urllib3.disable_warnings()
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
H={'User-Agent':'Mozilla/5.0'}
|
|
BASE='https://www.jejusi.go.kr'
|
|
|
|
def get(u):
|
|
return requests.get(u, timeout=20, headers=H, verify=False, allow_redirects=True)
|
|
|
|
def norm(href):
|
|
if not href: return ''
|
|
href=href.split('#')[0]
|
|
href=re.sub(r'[?&]_csrf=[0-9a-f\-]+','',href)
|
|
href=href.rstrip('?&')
|
|
if href.startswith('http'):
|
|
if 'www.jejusi.go.kr' in href:
|
|
href='/'+href.split('jejusi.go.kr/')[1]
|
|
else:
|
|
return href # external
|
|
return href
|
|
|
|
# 1) New /news/ LNB tree
|
|
print('===== NEW /news/ LNB (소통/시정소식 영역) =====')
|
|
r=get(BASE+'/news/communite/report.do')
|
|
soup=BeautifulSoup(r.content,'html.parser')
|
|
ul=soup.select_one('ul.depth1')
|
|
news_leaves=[]
|
|
if ul:
|
|
for li in ul.find_all('li', recursive=False):
|
|
a=li.find('a', recursive=False)
|
|
if not a: continue
|
|
top=a.get_text(strip=True); tophref=norm(a.get('href'))
|
|
print(f'[{top}] {tophref}')
|
|
sub=li.find('ul', recursive=False)
|
|
if sub:
|
|
for a2 in sub.find_all('a'):
|
|
t2=a2.get_text(strip=True); h2=norm(a2.get('href'))
|
|
tb=' (새창)' if a2.get('target')=='_blank' else ''
|
|
print(f' - {t2} {h2}{tb}')
|
|
news_leaves.append((top,t2,h2,bool(a2.get('target')=='_blank')))
|
|
else:
|
|
print('!! ul.depth1 not found')
|
|
|
|
# 2) Excel current 시정소식/mainNews rows
|
|
print('\n===== EXCEL rows with /mainNews/ or /news/ =====')
|
|
wb=openpyxl.load_workbook('제주특별자치도_제주시.xlsx'); ws=wb.active
|
|
xl_paths=set()
|
|
for rr in range(3, ws.max_row+1):
|
|
K=ws.cell(rr,11).value or ''
|
|
djk=[ws.cell(rr,c).value for c in range(4,11)]
|
|
djk=[str(x) for x in djk if x not in (None,'')]
|
|
p=norm(K)
|
|
xl_paths.add(p)
|
|
if '/mainNews' in p or p.startswith('/news'):
|
|
print(f'r{rr}', ' / '.join(djk), '||', p)
|
|
|
|
# 3) GNB internal leaves vs excel (path-based, no redirect resolve)
|
|
print('\n===== GNB internal leaves MISSING from excel (path compare) =====')
|
|
r=get(BASE+'/main.do')
|
|
soup=BeautifulSoup(r.content,'html.parser')
|
|
nav=soup.find('nav')
|
|
seen=set(); missing=[]
|
|
for a in nav.find_all('a'):
|
|
h=norm(a.get('href')); t=a.get_text(strip=True)
|
|
if not h.startswith('/'): continue # internal only
|
|
if not h.endswith('.do'): continue
|
|
if h in seen: continue
|
|
seen.add(h)
|
|
if h not in xl_paths:
|
|
missing.append((t,h))
|
|
print(f'GNB internal .do leaves: {len(seen)}, missing from excel: {len(missing)}')
|
|
for t,h in missing:
|
|
print(f' MISS {t} {h}')
|