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

105 lines
4.8 KiB
Python

# -*- coding: utf-8 -*-
"""익산 게시판 행 M/N/O/P/Q 수집(행범위 지정). 글수(전체N)·N(이미지규칙+상세top10)·KOGL(open0N+표준+링크, 상세top10).
사용: python -X utf8 _익산_mnopq.py <start> <end> [--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 if False else M)
sk=importlib.util.spec_from_file_location('k',os.path.join(HERE,'_임실_kogl.py'))
KG=importlib.util.module_from_spec(sk); sk.loader.exec_module(KG)
XLSX=r'D:\01.프로젝트\DB수집\작업파일\광역_사이트맵\전북특별자치도\9.익산시\전북특별자치도_익산시.xlsx'
SESS=M.make_session()
TOT=re.compile(r'전체\s*([\d,]+)|총\s*([\d,]+)\s*건|총\s*게시물\s*([\d,]+)')
DECO=re.compile(r'/common/|move\.png|no[-_]?img|blank|spacer|/ico|/btn|bullet|arrow|/bg|icon|see_btn|/sample|mimetype|/file_|filedown|btn_dir|/open\d',re.I)
EXCL=re.compile(r'한눈에|흐름도|절차도|로고(?!송)|logo(?!song)|아이콘|배너|banner|신문고|relation_item|tracer|headline|copyright|copy_logo|popup|wa_mk|웹접근성|품질인증',re.I)
AUDIO=re.compile(r'\.(?:mp3|wav|m4a|ogg|flac)\b',re.I)
MAPPDF=re.compile(r'pdf|viewer\.html|/map|kakao|daum.*map',re.I)
def real_imgs(body):
out=[]
for img in body.find_all('img'):
src=img.get('src') or ''
if not src or M.KOGL_IMG_PAT.search(src) or DECO.search(src): continue
if EXCL.search(src+' '+(img.get('alt') or '')): continue
out.append(img)
return out
def media(body):
txt=len(body.get_text(strip=True))>30
img=len(real_imgs(body))>0; vid=False
for ifr in body.find_all('iframe'):
ss=ifr.get('src') or ''
if M.YOUTUBE_PAT.search(ss): vid=True
elif MAPPDF.search(ss): img=True
if not vid and (body.find('video') or body.find('a',href=M.YOUTUBE_PAT) or M.VIDEO_EXT.search(str(body))): vid=True
aud=bool(body.find('audio')) or bool(AUDIO.search(str(body)))
return img,vid,aud,txt
def njoin(t,i,v,a):
p=[]
if t:p.append('어문')
if i:p.append('이미지')
if v:p.append('영상')
if a:p.append('오디오')
return ','.join(p) if p else '없음'
def collect(K):
soup=None
for _ in range(3):
soup=M.fetch(SESS,K)
if soup is not None: break
if soup is None: return None
body=M.get_body(soup,M.BODY_SEL); txt=body.get_text(' ',strip=True)
mm=TOT.search(txt); Mval=None
if mm: g=[x for x in mm.groups() if x]; Mval=int(g[0].replace(',',''))
else:
el=body.select_one('.total strong,.num,.board_total')
if el and el.get_text(strip=True).replace(',','').isdigit(): Mval=int(el.get_text(strip=True).replace(',',''))
img,vid,aud,t=media(body)
types,qy,qn=KG.kogl(body); P='게시판' if types else ''
dus=KG.detail_urls(body,K,10)
empty=bool(re.search(r'게시물이?\s*없|등록된?\s*(?:게시물|자료)\s*가?\s*없',txt))
if (Mval==0) or (not dus and empty):
return {'M':0,'N':'없음','O':'미부착','P':'','Q':''}
for du in dus:
ds=M.fetch(SESS,du)
if not ds: continue
db=M.get_body(ds,M.BODY_SEL)
i2,v2,a2,t2=media(db); img=img or i2;vid=vid or v2;aud=aud or a2;t=t or t2
dt,dqy,dqn=KG.kogl(db)
if dt:
if not types: P='게시물'
types|=dt; qy=qy or dqy; qn=qn or dqn
N=njoin(t,img,vid,aud)
if types:
O=','.join('%d유형'%n for n in sorted(types)); Q='Y' if qy else 'N'; P=P or '게시판'
else: O='미부착'; P='';Q=''
return {'M':Mval if Mval is not None else 0,'N':N,'O':O,'P':P,'Q':Q}
def main():
st,en=int(sys.argv[1]),int(sys.argv[2]); write='--write' in sys.argv
wb=openpyxl.load_workbook(XLSX); ws=wb.active
jobs=[r for r in range(st,en+1) if isinstance(ws.cell(r,11).value,str) and ws.cell(r,11).value.startswith('http')]
res={}
with ThreadPoolExecutor(max_workers=6) as ex:
futs={ex.submit(collect,ws.cell(r,11).value):r for r in jobs}
for f in as_completed(futs):
r=futs[f]
try: res[r]=f.result()
except: res[r]=None
for r in sorted(res):
o=res[r]
print('r%d %s'%(r,o))
if write:
shutil.copy(XLSX, XLSX.replace('.xlsx','_backup_mnopq전.xlsx'))
for r,o in res.items():
if not o: continue
ws.cell(r,13).value=o['M']; ws.cell(r,14).value=o['N']
ws.cell(r,15).value=o['O']; ws.cell(r,16).value=o['P']; ws.cell(r,17).value=o['Q']
wb.save(XLSX); print('적용 %d'%len([1 for o in res.values() if o]))
if __name__=='__main__': main()