# -*- coding: utf-8 -*- import openpyxl, json, time, re, ssl, urllib.request, sys from urllib.parse import urljoin, urlparse from bs4 import BeautifulSoup ctx=ssl.create_default_context(); ctx.check_hostname=False; ctx.verify_mode=ssl.CERT_NONE HDR={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/124 Safari/537.36'} BASE='https://www.komipo.co.kr' def fetch(u): return urllib.request.urlopen(urllib.request.Request(u,headers=HDR),timeout=25,context=ctx).read() UI=re.compile(r'banner|/common/|icon|btn|logo|bullet|sprite|blank|no_img|arrow|/dot|/bg|popup|/sns',re.I) DIAG=re.compile(r'조직도|체계도|순서도|흐름도|구성도|개념도|체계$|절차도|한눈|로드맵|추진체계|다이어그램|chart|graph') def classify(url, self_label): full=urljoin(BASE+'/', url) if url.startswith('/') else url host=urlparse(full).netloc.lower() rec={'url':full} if host not in ('www.komipo.co.kr','komipo.co.kr'): rec.update(L='사이트',M=None,N='',O=''); return rec if '/board/' in full and 'boardMain' in full: try: soup=BeautifulSoup(fetch(full),'html.parser') em=soup.select_one('div.total em.count') or soup.select_one('.total .count') M=int(re.sub(r'[^0-9]','',em.get_text())) if em else 0 except Exception as e: M=0; print(' board err',full,str(e)[:40],file=sys.stderr) # N: list thumbnails N='어문' try: lst=soup.select_one('#content') or soup thumbs=[im for im in lst.find_all('img') if not UI.search(im.get('src','')) and 'getImage' in im.get('src','')] if M>0 and thumbs: N='어문,이미지' if M==0: N='없음' except: pass rec.update(L='게시판',M=M,N=N,O='미부착'); return rec # page try: soup=BeautifulSoup(fetch(full),'html.parser') cont=soup.select_one('#content') or soup.select_one('div.contents') imgs=cont.find_all('img') if cont else [] real=[] for im in imgs: src=im.get('src','');alt=im.get('alt','') if UI.search(src): continue if 'getImage' in src and ('코로나' in alt or '정규직' in alt): continue # alio popups real.append((src,alt)) diag_only = real and all(DIAG.search(a or '') for s,a in real) N='어문,이미지' if (real and not diag_only) else '어문' rec.update(L='페이지',M=1,N=N,O='미부착',_imgs=[a or s.split('/')[-1] for s,a in real][:6]); return rec except Exception as e: print(' page err',full,str(e)[:40],file=sys.stderr) rec.update(L='페이지',M=1,N='어문',O='미부착'); return rec # load found tabs + leaf info found=json.load(open('tabscan.json',encoding='utf-8')) wb=openpyxl.load_workbook('한국중부발전.xlsx');ws=wb.active # effective D-J eff={} for r in range(3,ws.max_row+1): for c in range(4,11): eff[(r,c)]=ws.cell(r,c).value for mr in ws.merged_cells.ranges: if mr.min_col>=4 and mr.max_col<=10 and mr.min_row>=3: top=ws.cell(mr.min_row,mr.min_col).value for rr in range(mr.min_row,mr.max_row+1): eff[(rr,mr.min_col)]=top # existing urls (normalized no query) -> for sibling dup check existing={} for r in range(3,ws.max_row+1): k=ws.cell(r,11).value if k: existing.setdefault(str(k).split('?')[0],r) plan=[] for r_s,v in sorted(found.items(),key=lambda x:int(x[0])): r=int(r_s) cols={c:eff[(r,c)] for c in range(4,11) if eff[(r,c)] not in (None,'')} lc=max(cols); leaf=cols[lc] tabs=v['tabs'] selfi=next((i for i,t in enumerate(tabs) if t['current']),0) self_label=tabs[selfi]['label'] mode='sibling' if self_label.strip()==str(leaf).strip() else 'child' parent_vals={c:eff[(r,c)] for c in range(4,11)} exp={'row':r,'b':ws.cell(r,2).value,'leaf':leaf,'lc':lc,'self':self_label,'mode':mode,'parent_path':[eff[(r,c)] for c in range(4,11)],'parent_K':ws.cell(r,11).value,'parent_LMNO':[ws.cell(r,c).value for c in (12,13,14,15)],'newrows':[]} for i,t in enumerate(tabs): full=urljoin(BASE+'/',t['href']) if t['href'].startswith('/') else t['href'] is_self=(i==selfi) nk=str(full).split('?')[0] if mode=='sibling' and not is_self and nk in existing: continue # already in sheet if mode=='sibling' and is_self: continue # anchor already there if is_self and mode=='child': cl={'L':exp['parent_LMNO'][0],'M':exp['parent_LMNO'][1],'N':exp['parent_LMNO'][2],'O':exp['parent_LMNO'][3],'url':full} else: cl=classify(t['href'],self_label); time.sleep(0.7) exp['newrows'].append({'label':t['label'],'is_self':is_self,**cl}) plan.append(exp) json.dump(plan,open('plan.json','w',encoding='utf-8'),ensure_ascii=False,indent=1) # print summary for e in plan: print(f"r{e['row']} b{e['b']} [{e['mode']}] leaf='{e['leaf']}' self='{e['self']}'") for nr in e['newrows']: tag='(self)' if nr['is_self'] else '' print(f" +{nr['label']!r}{tag} L={nr.get('L')} M={nr.get('M')} N={nr.get('N')} {nr.get('_imgs','')}") print('\n총 신규/재구성 행:', sum(len(e['newrows']) for e in plan))