DB_JOB/작업파일/완료/광역_사이트맵/충청북도/_cb_kogl.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

67 lines
2.9 KiB
Python

# -*- coding: utf-8 -*-
# 충북 범용 공공누리(O) 본문영역 재검출: python _cb_kogl.py <엑셀> <host>
# 본문 컨테이너(#contents 등) 안의 img_opentype0N 만 인정(푸터 전역마크 제외). 게시판은 상세글 확인.
import openpyxl, sys, io, re, ssl, time, urllib.request
from bs4 import BeautifulSoup
from concurrent.futures import ThreadPoolExecutor
sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8')
F=sys.argv[1]; HOST=sys.argv[2]
ctx=ssl.create_default_context(); ctx.check_hostname=False; ctx.verify_mode=ssl.CERT_NONE
def get(u):
for _ in range(2):
try:
req=urllib.request.Request(u,headers={'User-Agent':'Mozilla/5.0'})
return urllib.request.urlopen(req,timeout=20,context=ctx).read().decode('utf-8','ignore')
except Exception: time.sleep(0.5)
return ''
CONTENT_SEL='#contents,.contents,#content,.content,.sub_content,.sub_contents,.contents_in,.bbs_view,.board_view,.view_con,.cont_area'
def opentypes_in_content(html):
s=BeautifulSoup(html,'html.parser')
cont=s.select_one(CONTENT_SEL)
area=cont if cont else s
# 본문영역 내 opentype 이미지
found=set()
for img in area.find_all('img'):
src=(img.get('src') or '')+' '+(img.get('alt') or '')
m=re.search(r'opentype0?(\d)',src)
if m: found.add(int(m.group(1)))
return sorted(found)
wb=openpyxl.load_workbook(F); ws=wb.active
def detail_url(html):
s=BeautifulSoup(html,'html.parser')
for a in s.find_all('a'):
href=a.get('href') or ''
if re.search(r'BbsNttView|addBbsNttView|nttNo=|nttId=|BoardArticle|selectBoardArticle',href):
if href.startswith('http'): return href
if href.startswith('/'): return 'https://'+HOST+href
return None
def work(r):
L=ws.cell(r,12).value; url=ws.cell(r,11).value
if L not in ('페이지','게시판') or not str(url or '').startswith('http'): return (r,None)
h=get(url)
if not h: return (r,None)
if L=='게시판':
# 상세글 최대 3개 확인
types=set(opentypes_in_content(h))
du=detail_url(h); n=0
while du and n<3:
dh=get(du); types|=set(opentypes_in_content(dh))
nx=detail_url(dh); du=nx if nx and nx!=du else None; n+=1
if types: break
return (r,sorted(types))
else:
return (r,opentypes_in_content(h))
rows=[r for r in range(3,ws.max_row+1) if ws.cell(r,12).value in('페이지','게시판')]
print('O검출 대상',len(rows),flush=True)
results={}
with ThreadPoolExecutor(max_workers=8) as ex:
for r,t in ex.map(work,rows): results[r]=t
chg=0
for r,t in results.items():
old=ws.cell(r,15).value
new=','.join('%d유형'%x for x in t) if t else '미부착'
if new!=old: ws.cell(r,15).value=new; chg+=1
wb.save(F)
att=sum(1 for r in results if results[r])
print('O 본문부착 %d행 | 변경 %d'%(att,chg))