"""전북 미해결 사이트 2차 정밀 탐색.""" import re, ssl, sys, warnings from urllib.parse import urljoin, urlparse import requests from requests.adapters import HTTPAdapter from urllib3.util.ssl_ import create_urllib3_context from bs4 import BeautifulSoup warnings.filterwarnings('ignore') UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' def sess(): s = requests.Session(); s.headers.update({'User-Agent': UA}); return s def fetch(s, url, t=20): r = s.get(url, timeout=t, verify=False, allow_redirects=True) meta = re.search(rb']*charset=["\']?\s*([\w-]+)', r.content[:4096], re.I) r.encoding = meta.group(1).decode('ascii','ignore') if meta else r.apparent_encoding return r # 사이트맵 링크를 못 찾은 사이트: 인덱스 HTML에서 sitemap/menuCd/전체메뉴 힌트 검색 NOLINK = { '군산시': 'https://www.gunsan.go.kr/main', '남원시': 'https://www.namwon.go.kr/index.do?menuUid=ff8080818e3beff0018e40e8f63e02d2', '부안군': 'https://www.buan.go.kr/index.buan?contentsSid=1', '완주군': 'https://www.wanju.go.kr/index.9is', '임실군': 'https://www.imsil.go.kr/index.imsil', '정읍시': 'https://www.jeongeup.go.kr/index.jeongeup', } def probe_links(name, url): print(f'\n{"="*70}\n[{name}] {url}') s = sess() try: r = fetch(s, url) except Exception as e: print(' fetch fail', e); return base = f'{urlparse(r.url).scheme}://{urlparse(r.url).netloc}' soup = BeautifulSoup(r.text, 'html.parser') # sitemap/전체메뉴 텍스트나 href를 가진 a 전부 hits = [] for a in soup.find_all('a'): txt = (a.get_text() or '').strip() href = a.get('href','') or '' oc = a.get('onclick','') or '' blob = f'{txt}|{href}|{oc}' if re.search(r'사이트맵|전체메뉴|sitemap|site_map|allmenu', blob, re.I): hits.append((txt[:20], href[:90], oc[:90])) for h in hits[:15]: print(' a:', h) # menuCd 패턴 가진 href 중 sitemap 후보 (DOM_...02000000 류) cds = set(re.findall(r'menuCd=DOM_\d+', r.text)) print(' menuCd 샘플:', list(cds)[:10]) for n, u in NOLINK.items(): probe_links(n, u) # ---- 구조 깊이 확인이 필요한 사이트들 ---- DEEP = { '고창군': 'https://www.gochang.go.kr/index.gochang?menuCd=DOM_000000106006000000', '익산시': 'https://www.iksan.go.kr/index.do?menuUid=ff80808199f0d11c019a041b8e35174a', '전주시': 'https://www.jeonju.go.kr/index.9is?contentUid=ff8080818c7c2e8e018c7fd67b9a03b6', '순창군': 'https://www.sunchang.go.kr/', '진안군': 'https://www.jinan.go.kr/index.jinan?menuCd=DOM_000000110002000000', } def deep(name, url, sel): print(f'\n{"#"*70}\n[{name}] DEEP {url} sel={sel}') s = sess() try: r = fetch(s, url) except Exception as e: print(' fail', e); return soup = BeautifulSoup(r.text, 'html.parser') cont = soup.select_one(sel) if not cont: print(' 컨테이너 없음'); return # 첫 블록 하나만 골라 li/a href까지 자세히 print(cont.prettify()[:2500]) deep('고창군 첫메뉴', 'https://www.gochang.go.kr/index.gochang?menuCd=DOM_000000106006000000', 'div.sitemap div.menu1') deep('익산시 group수', 'https://www.iksan.go.kr/index.do?menuUid=ff80808199f0d11c019a041b8e35174a', 'div.sitemap_group') deep('전주시', 'https://www.jeonju.go.kr/index.9is?contentUid=ff8080818c7c2e8e018c7fd67b9a03b6', 'div.sitemap_Warp') deep('순창군 gnb', 'https://www.sunchang.go.kr/', 'div.sitemap_box ul.gnb_list') deep('진안군 첫dl', 'https://www.jinan.go.kr/index.jinan?menuCd=DOM_000000110002000000', 'div.sitemap dl')