# -*- coding: utf-8 -*- # 충북형(eGov 메가메뉴) 범용 수집: 트리 + 본문탭 + L/M/O # 사용: python _cb_crawl.py import sys,io,json,re,time,urllib.request,ssl from bs4 import BeautifulSoup sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8') ctx=ssl.create_default_context(); ctx.check_hostname=False; ctx.verify_mode=ssl.CERT_NONE HOST=sys.argv[1]; SITEMAP=sys.argv[2]; OUT=sys.argv[3] BASE='https://'+HOST def absu(u): if not u: return u if u.startswith('http'): return u if u.startswith('/'): return BASE+u return BASE+'/'+u _cache={} def get(u): if u in _cache: return _cache[u] h='' for _ in range(2): try: req=urllib.request.Request(u,headers={'User-Agent':'Mozilla/5.0'}) h=urllib.request.urlopen(req,timeout=25,context=ctx).read().decode('utf-8','ignore'); break except Exception: time.sleep(1) _cache[u]=h; return h def kind(u): if not u: return 'none' if u.startswith('http') and HOST not in u: return '사이트' if 'eminwon' in u: return '사이트' if re.search(r'selectBbsNttList|selectOthbcInfoList|BbsNtt',u): return '게시판' return '페이지' def board_M(html): m=re.search(r'게시물[^\d]{0,15}([\d,]+)', html) if m: try: return int(m.group(1).replace(',','')) except: return 0 return 0 def has_opentype(html): # full-page(빠른 1차); 본문한정은 _cb_kogl로 후처리 mm=re.findall(r'img_opentype0?(\d)', html); return sorted(set(int(x) for x in mm)) if mm else [] def page_tabs(html, self_url): # 본문 콘텐츠 탭만: 탭바가 '자기 페이지 key'를 포함(self-referential)해야 인정 → 전역위젯 배제 s=BeautifulSoup(html,'html.parser') cont=s.select_one('#contents,.contents,.sub_content,.sub_contents,#content,.content_in') or s selfk=re.search(r'key=(\d+)',self_url); selfk=selfk.group(1) if selfk else None if not selfk: return [] for ul in cont.find_all('ul'): cls=' '.join(ul.get('class') or []) if not re.search(r'tab',cls,re.I): continue anchors=ul.find_all('a') if len(anchors)<2: continue keys=[re.search(r'key=(\d+)',a.get('href') or '') for a in anchors] keys=[k.group(1) for k in keys if k] # 가드: 탭앵커가 전부 key= 형식이고 self key를 포함해야 진짜 본문탭 if selfk not in keys: continue if len(keys)=3: child[4]=lab else: child[depth+1]=lab nodes.append({'d':child,'url':tu,'kind':tk,'M':tM,'O':tO,'det':tdet,'tab':True}) if i%25==0: print('%d/%d'%(i+1,len(tree)),flush=True) json.dump(nodes,open(OUT,'w',encoding='utf-8'),ensure_ascii=False) from collections import Counter print('총행',len(nodes),'| L',dict(Counter(n['kind'] for n in nodes)),'| 탭',sum(1 for n in nodes if n.get('tab')),'| O',sum(1 for n in nodes if n['O']))