- 공공기관2/3 작업본 + 오늘 제출 17곳 D~J 카테고리 셀병합 정상화 - 한국지역난방공사 옵션2(고아셀 F98 수정)+전행 높이17 - 제출_프리랜서2_2026-06-21.zip 생성(17개 xlsx, 2,468행) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
67 lines
3.6 KiB
Python
67 lines
3.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
import openpyxl, time, json, re, ssl, urllib.request, sys
|
|
from urllib.parse import 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'}
|
|
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|/mark|webwatch',re.I)
|
|
DIAG=re.compile(r'조직도|체계도|순서도|흐름도|구성도|개념도|체계$|절차도|로드맵|추진체계|다이어그램|한눈|chart|graph|플랫폼\s*구축|구성$')
|
|
|
|
ws=openpyxl.load_workbook('한국중부발전.xlsx').active
|
|
rows=[]
|
|
for r in range(3,ws.max_row+1):
|
|
if all(ws.cell(r,c).value in (None,'') for c in range(2,16)): continue
|
|
rows.append((r,ws.cell(r,2).value,ws.cell(r,11).value,ws.cell(r,12).value,ws.cell(r,13).value,ws.cell(r,14).value))
|
|
out=[]
|
|
for (r,b,K,L,M,N) in rows:
|
|
if not K or 'komipo.co.kr' not in str(K): # 사이트/외부
|
|
continue
|
|
try: soup=BeautifulSoup(fetch(str(K)),'html.parser')
|
|
except Exception as e: out.append((r,b,'FETCHERR',L,M,N,str(e)[:30])); time.sleep(0.4); continue
|
|
isboard='/board/' in str(K) and ('boardMain' in str(K) or 'boardList' in str(K))
|
|
isfr='/fr/' in str(K)
|
|
rL=rM=rN=None
|
|
if isboard or isfr:
|
|
em=soup.select_one('div.total em.count') or soup.select_one('.total .count')
|
|
rM=int(re.sub(r'[^0-9]','',em.get_text())) if em else 0
|
|
rL='게시판'
|
|
cont=soup.select_one('#content') or soup
|
|
thumbs=[im for im in cont.find_all('img') if 'getImage' in im.get('src','') and not UI.search(im.get('src',''))]
|
|
rN='없음' if rM==0 else ('어문,이미지' if thumbs else '어문')
|
|
else:
|
|
# in-page tabs?
|
|
ntab=0
|
|
for ul in soup.find_all('ul'):
|
|
cls=' '.join(ul.get('class') or [])
|
|
if 'tab' in cls and 'tab_link' not in cls:
|
|
tt=[li for li in ul.find_all('li',recursive=False) if li.find('a') and (li.find('a').get('data-tab') or (li.find('a').get('href','') or '').startswith('#'))]
|
|
if len(tt)>=2: ntab=max(ntab,len(tt))
|
|
rL='페이지'; rM=ntab if ntab>=2 else 1
|
|
cont=soup.select_one('#content') or soup.select_one('div.contents')
|
|
real=[]
|
|
if cont:
|
|
for im in cont.find_all('img'):
|
|
src=im.get('src','');alt=im.get('alt','')
|
|
if UI.search(src): continue
|
|
if 'getImage' in src and ('코로나' in alt or '정규직' in alt): continue
|
|
real.append((src,alt))
|
|
diag_only=real and all(DIAG.search(a or '') for s,a in real)
|
|
rN='어문,이미지' if (real and not diag_only) else '어문'
|
|
# compare (normalize trailing spaces)
|
|
sN=(N or '').strip()
|
|
flags=[]
|
|
if rL!=(L or '').strip(): flags.append('L:%s!=%s'%(L,rL))
|
|
if rM!=M: flags.append('M:%s!=%s'%(M,rM))
|
|
if rN!=sN: flags.append('N:%s!=%s'%(sN,rN))
|
|
if flags:
|
|
out.append((r,b,'DIFF',L,M,sN,rL,rM,rN,' '.join(flags),str(K)[:46],[a or s.split('/')[-1] for s,a in (real if not (isboard or isfr) else [])][:4]))
|
|
time.sleep(0.55)
|
|
json.dump(out,open('recheck.json','w',encoding='utf-8'),ensure_ascii=False,indent=1,default=str)
|
|
print('대조 mismatch:',len([x for x in out if x[2]=='DIFF']),'/ fetch err:',len([x for x in out if x[2]=='FETCHERR']))
|
|
for x in out:
|
|
if x[2]=='DIFF':
|
|
print(f'r{x[0]} b{x[1]} [{x[9]}] {x[10]} {x[11]}')
|
|
else:
|
|
print(f'r{x[0]} b{x[1]} FETCHERR {x[6]}')
|