DB_JOB/_스크립트/_buyeo_rejudge_N.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

141 lines
5.2 KiB
Python

# -*- coding: utf-8 -*-
"""부여군 N(저작물 유형) 재판정 — 장식/공통UI 이미지 false '이미지' 제거.
- phase234 detect_media 는 KOGL 마크만 제외 → file_icon.gif·see_btn.gif·btn_page_*.gif 등
게시판 스킨/공통버튼 아이콘을 '이미지'로 오탐.
- [[feedback_N_image_rule]] 적용: 장식/공통UI·페이징버튼·첨부아이콘 제외, 실제 사진/업로드·지도/PDF임베드만 인정.
- 대상: 현재 N 에 '이미지' 포함된 같은도메인 행만(false 양성 제거 전용 → 이미지 추가 안 함).
- 게시판은 상세글 상위5 추적. body=#txt|#contents|main.
사용: python -X utf8 _buyeo_rejudge_N.py [--write]
"""
import sys, warnings, re
from urllib.parse import urljoin
import requests, openpyxl
from bs4 import BeautifulSoup
warnings.filterwarnings('ignore')
S = requests.Session(); S.headers.update({'User-Agent': 'Mozilla/5.0'})
XLSX = r'D:\01.프로젝트\DB수집\작업파일\광역_사이트맵\충청남도\7.부여군\충청남도_부여군.xlsx'
DOMAIN = 'buyeo.go.kr'
KOGL = re.compile(r'(?:new_)?img_open(?:type|code)\d', re.I)
# 장식/공통UI: 기존 논산 DECO + 부여 게시판 스킨/공통버튼/첨부아이콘 패턴 보강
DECO = re.compile(
r'/site/common/img/|move\.png|no[-_]?img|blank\.|spacer\.|'
r'/img/(?:icon|ico|bul|bullet|arrow|btn|bg|tit|h\d)|'
r'ico_|btn_|bul_|bg_|_bg\.|icon_|'
r'file_icon|_icon\.|icon\.gif|_btn\.|see_btn|' # 첨부아이콘·바로보기버튼
r'/skin/|/images/(?:kr/)?common/|/common/img/|' # 게시판 스킨·공통 UI 디렉터리
r'btn_page|page_(?:next|prev|first|last)', re.I) # 페이징 버튼
MAPPDF = re.compile(r'pdf|viewer\.html|map|kakao|daum|naver.*map|google.*map|/map', re.I)
VIDEO = re.compile(r'youtube\.com|youtu\.be|/embed/|\.mp4|\.webm|vimeo', re.I)
DETAIL = re.compile(r'mode=V|view\.do|/view|seq=|idx=|mng_no=|bbsSeq|nttId|no=', re.I)
def get(url):
r = S.get(url, verify=False, timeout=20)
return BeautifulSoup(r.content, 'html.parser')
def body(soup):
return soup.select_one('#txt') or soup.select_one('#contents') or soup.select_one('main') or soup
def real_imgs(node):
n = 0
for im in node.find_all('img'):
src = im.get('src') or ''
if not src or KOGL.search(src) or DECO.search(src):
continue
n += 1
return n
def media_flags(node):
img = real_imgs(node) > 0
vid = False
for ifr in node.find_all('iframe'):
s = ifr.get('src') or ''
if VIDEO.search(s):
vid = True
elif MAPPDF.search(s):
img = True
for a in node.find_all('a'):
if VIDEO.search(a.get('href') or ''):
vid = True
if node.find('video'):
vid = True
return img, vid
def detail_links(soup, base):
out = []
for a in body(soup).find_all('a'):
h = a.get('href') or ''
if h.startswith('#'):
continue
if DETAIL.search(h):
out.append(urljoin(base, h))
seen = set(); res = []
for u in out:
if u not in seen:
seen.add(u); res.append(u)
return res[:5]
def judge(k, L, M, N):
if not isinstance(k, str) or DOMAIN not in k:
return N, 'skip(외부)'
try:
soup = get(k)
except Exception as e:
return N, f'fetch실패:{str(e)[:25]}'
bd = body(soup)
txtlen = len(bd.get_text(strip=True))
if L == '게시판' and (M in (0, '0', None)):
return '없음', 'board0'
has_img, has_vid = media_flags(bd)
detail = False
if L == '게시판' and not has_img:
for du in detail_links(soup, k):
try:
di, dv = media_flags(body(get(du)))
if di: has_img = True
if dv: has_vid = True
if di or dv: detail = True
if has_img and has_vid: break
except Exception:
pass
parts = []
if txtlen >= 1: parts.append('어문')
if has_img: parts.append('이미지')
if has_vid: parts.append('영상')
return (','.join(parts) if parts else '없음'), ('detail' if detail else '')
def main():
write = '--write' in sys.argv
wb = openpyxl.load_workbook(XLSX); ws = wb.active
changes = []
for r in range(3, ws.max_row + 1):
N = ws.cell(r, 14).value
if not (isinstance(N, str) and '이미지' in N):
continue
k = ws.cell(r, 11).value; L = ws.cell(r, 12).value; M = ws.cell(r, 13).value
new, tag = judge(k, L, M, N)
if new != N:
changes.append((r, N, new, tag, ws.cell(r, 7).value or ws.cell(r, 6).value, k))
print(f'현재 N에 이미지 포함 행 재판정 → 변경 {len(changes)}')
for r, old, new, tag, label, k in changes:
print(f'{r} [{old}{new}] {tag} | {label} | {str(k)[-45:]}')
if write and changes:
for r, old, new, tag, label, k in changes:
ws.cell(r, 14).value = new
wb.save(XLSX)
print(f'\n저장: {XLSX} ({len(changes)}건 수정)')
elif not write:
print('\n(계획만. --write 로 반영)')
if __name__ == '__main__':
main()