# -*- coding: utf-8 -*- """임실 공공누리(O/P/Q) 전수 재스캔. 임실 마크=open0N.jpg(alt 공공누리). 기존 img_opentypeN 정규식이 못잡음. 전 행(페이지+게시판, 게시판은 상세글 상위10) 재검. 매뉴얼 Phase4. 사용: python -X utf8 _임실_kogl.py [--write] """ import sys, os, re, shutil, importlib.util from urllib.parse import urljoin from concurrent.futures import ThreadPoolExecutor, as_completed import openpyxl HERE=os.path.dirname(os.path.abspath(__file__)) s=importlib.util.spec_from_file_location('m',os.path.join(HERE,'_jeonbuk_phase234_all.py')) M=importlib.util.module_from_spec(s); s.loader.exec_module(M) XLSX=r'D:\01.프로젝트\DB수집\작업파일\광역_사이트맵\전북특별자치도\10.임실군\전북특별자치도_임실군.xlsx' SESS=M.make_session() STD=re.compile(r'(?:new_)?img_open(?:type|code)(\d{1,2})\.(?:png|jpe?g|gif)',re.I) OPEN=re.compile(r'/open0?(\d)\.(?:jpg|jpeg|png|gif)',re.I) ALT=re.compile(r'제?(\d)\s*유형') LINK=re.compile(r'kogl\.or\.kr/info/licenseType(\d)',re.I) DETAIL=re.compile(r'view\.imsil|/view\.|articleNo=|dataSid=|nttId=|not_ancmt|bbsView|seqRepeat=|mode=V',re.I) def kogl(body): types=set(); qy=False; qn=False for img in body.find_all('img'): src=img.get('src') or ''; alt=img.get('alt') or '' t=None mm=STD.search(src) if mm: t=int(mm.group(1)) elif ('공공누리' in alt) or re.search(r'/open\d',src): m2=OPEN.search(src) or ALT.search(alt) if m2: t=int(m2.group(1)) if t and 1<=t<=4: types.add(t) par=img.find_parent('a') if par and par.get('href','') and not par['href'].startswith(('#','javascript')): qy=True else: qn=True for a in body.find_all('a',href=True): mm=LINK.search(a['href']) if mm: types.add(int(mm.group(1))); qy=True return types, qy, qn def detail_urls(body, base, limit=10): urls=[]; seen=set() for a in body.find_all('a',href=True): h=a['href'] if h and not h.startswith('#') and DETAIL.search(h): full=urljoin(base,h) if full not in seen: seen.add(full); urls.append(full) if len(urls)>=limit: break return urls def scan_row(K, L): soup=M.fetch(SESS,K) if soup is None: return None body=M.get_body(soup,M.BODY_SEL) types,qy,qn=kogl(body); P='게시판' if types else '' if L=='게시판': for du in detail_urls(body,K,10): ds=M.fetch(SESS,du) if not ds: continue db=M.get_body(ds,M.BODY_SEL) dt,dqy,dqn=kogl(db) if dt: if not types: P='게시물' types|=dt; qy=qy or dqy; qn=qn or dqn if not types: return ('미부착','', '') O=','.join('%d유형'%n for n in sorted(types)) Q='Y' if qy else 'N' return (O, P or '게시판', Q) def main(): write='--write' in sys.argv wb=openpyxl.load_workbook(XLSX); ws=wb.active jobs=[] for r in range(3,ws.max_row+1): K=ws.cell(r,11).value; L=ws.cell(r,12).value if isinstance(K,str) and K.startswith('http') and L in ('페이지','게시판'): jobs.append((r,K,L)) res={} with ThreadPoolExecutor(max_workers=6) as ex: futs={ex.submit(scan_row,K,L):r for r,K,L in jobs} for f in as_completed(futs): r=futs[f] try: res[r]=f.result() except: res[r]=None found=[] for r in sorted(res): v=res[r] if v is None: continue O,Pp,Qq=v cur=ws.cell(r,15).value if O!='미부착' and O!=(cur or '미부착'): found.append((r,cur,O,Pp,Qq)) print('공공누리 발견(기존과 다름) %d행:'%len(found)) for r,cur,O,Pp,Qq in found: print(' r%d %s: %s→%s (P=%s Q=%s)'%(r,ws.cell(r,7).value or ws.cell(r,6).value,cur,O,Pp,Qq)) if write and found: shutil.copy(XLSX, XLSX.replace('.xlsx','_backup_kogl재스캔전.xlsx')) for r,cur,O,Pp,Qq in found: ws.cell(r,15).value=O; ws.cell(r,16).value=Pp; ws.cell(r,17).value=Qq wb.save(XLSX); print('적용 %d행 · 백업'%len(found)) if __name__=='__main__': main()