DB_JOB/작업파일/완료/광역_사이트맵/전북특별자치도/13.정읍시/_crawl.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

62 lines
2.7 KiB
Python

"""Unified crawl of all 정읍시 rows.
For each menuCd URL: capture bbs_total count (board vs page), redirect-loop flag.
Aggregate left-nav (ul.dep2/3/4) anchor target=_blank across ALL pages -> menuCd map.
Outputs _report.json for review before writing the xlsx."""
import urllib.request, urllib.error, ssl, re, io, sys, json
from concurrent.futures import ThreadPoolExecutor
from _navmap import NavParser
ctx = ssl.create_default_context(); ctx.check_hostname = False; ctx.verify_mode = ssl.CERT_NONE
BASE = 'https://www.jeongeup.go.kr/index.jeongeup?menuCd='
def fetch(url):
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
return urllib.request.urlopen(req, timeout=25, context=ctx).read().decode('utf-8', 'replace')
TOTAL_RE = re.compile(r'bbs_total[^>]*>[^<]*<strong>\s*([\d,]+)\s*</strong>')
def crawl_one(item):
r, code, url = item
out = {'r': r, 'code': code, 'count': None, 'err': None, 'nav': []}
try:
h = fetch(url)
except urllib.error.HTTPError as e:
out['err'] = 'redirect_loop' if e.code in (301, 302) else 'http%d' % e.code
return out
except Exception as e:
out['err'] = type(e).__name__
return out
m = TOTAL_RE.search(h)
if m:
out['count'] = int(m.group(1).replace(',', ''))
p = NavParser(); p.feed(h)
out['nav'] = [(a['code'], a['blank']) for a in p.results if a['code']]
return out
if __name__ == '__main__':
data = json.load(io.open('_dump.json', encoding='utf-8'))
items = []
for row in data:
k = row.get('K', '')
mc = re.search(r'menuCd=(DOM_\d+)', k)
if mc and 'jeongeup.go.kr/index.jeongeup' in k:
items.append((row['r'], mc.group(1), k))
results = []
with ThreadPoolExecutor(max_workers=8) as ex:
for res in ex.map(crawl_one, items):
results.append(res)
# aggregate nav target map across all pages
navmap = {}
for res in results:
for code, blank in res['nav']:
navmap.setdefault(code, set()).add(blank)
navblank = {c: (True in v) for c, v in navmap.items()}
report = {'pages': [{'r': x['r'], 'code': x['code'], 'count': x['count'], 'err': x['err']} for x in results],
'navblank': navblank}
json.dump(report, io.open('_report.json', 'w', encoding='utf-8'), ensure_ascii=False, indent=0)
errs = [x for x in results if x['err']]
counts = [x for x in results if x['count'] is not None]
print('crawled:', len(results), '| with bbs_total:', len(counts), '| errors:', len(errs))
print('distinct nav menuCd:', len(navblank), '| nav target=_blank:', sum(1 for v in navblank.values() if v))
print('errors:', [(x['r'], x['err']) for x in errs])