# -*- coding: utf-8 -*- """검출된 본문이미지가 '진짜 사진'인지 크기로 검증. 아이콘·작은배너·서명 제외. 기준: 가로>=250 AND 세로>=250 (또는 한 변>=400) 그리고 용량>=12KB → 사진으로 인정. JPEG/PNG/GIF 헤더만 읽어 치수 파악(전체 다운로드 최소화는 생략, 작은파일이라 통째 읽음). 사용: python _imgcheck.py "" (해당 보드 본문이미지 표본 검사) """ import urllib.request, ssl, re, html as ht, sys, json, struct, io ctx=ssl.create_default_context(); ctx.check_hostname=False; ctx.verify_mode=ssl.CERT_NONE BASE='https://www.gimje.go.kr' def get_bytes(url): req=urllib.request.Request(url, headers={'User-Agent':'Mozilla/5.0'}) return urllib.request.urlopen(req,context=ctx,timeout=25).read() def get(url): return get_bytes(url).decode('utf-8','replace') def img_size(data): """(w,h) from JPEG/PNG/GIF bytes, else (None,None).""" if data[:8]==b'\x89PNG\r\n\x1a\n': w,h=struct.unpack('>II', data[16:24]); return w,h if data[:3]==b'GIF': w,h=struct.unpack('HH', data[i+5:i+9]); return w,h seg=struct.unpack('>H', data[i+2:i+4])[0]; i+=2+seg return None,None def is_real_photo(url): if url.startswith('/'): url=BASE+url try: data=get_bytes(url) except Exception as ex: return None, f'ERR {ex}' w,h=img_size(data); kb=len(data)//1024 if w is None: return None, f'치수불명 {kb}KB' real = (kb>=12) and ((w>=250 and h>=250) or max(w,h)>=400) return real, f'{w}x{h} {kb}KB' # 본문이미지 추출(스캐너와 동일 규칙) def view_links(menucd): d=get(f'{BASE}/index.gimje?menuCd={menucd}') out=[] for l in re.findall(r'href="(/board/view\.gimje\?[^"]+)"', d): l=ht.unescape(l) if l not in out: out.append(l) return out def body_content_imgs(det): s=det.find('bbs_view') if s<0: s=det.find('bbs_con') if s<0: return [] ends=[p for p in (det.find('bbs_file',s),det.find('bbs_btn',s),det.find('bbs_navi',s),det.find('bbs_paging',s)) if p>0] seg=det[s:(min(ends) if ends else s+12000)] out=[] for i in re.findall(r']+src="([^"]+)"', seg): i=i.strip(); low=i.lower() if '/images/common/' in low or 'opentype' in low: continue if any(k in low for k in ('icon','emoticon','blank.','spacer','btn_','/common/','logo')): continue if '/upload_data/' in low or '/editor' in low or 'board_data' in low or '/se2/' in low: out.append(i) return out if __name__=='__main__': targets=json.loads(sys.argv[1]) for row,menucd,label in targets: links=view_links(menucd)[:10] photo_posts=0; checked_imgs=[] for l in links: try: det=get(BASE+l) except: continue imgs=body_content_imgs(det) postreal=False for im in imgs[:2]: real,info=is_real_photo(im) checked_imgs.append((real,info,im.split('/')[-1][:24])) if real: postreal=True if postreal: photo_posts+=1 verdict='어문,이미지' if photo_posts>0 else '어문' print(f'행{row} | {label[:28]} | 진짜사진게시물 {photo_posts} → {verdict}') for real,info,nm in checked_imgs[:5]: mark='📷' if real else ('?' if real is None else '🔸아이콘') print(f' {mark} {info} {nm}')