공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
118 lines
5.1 KiB
Python
118 lines
5.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""서산 bbs_category_list 부모-카테고리 보드 전개 + 카테고리행 L/M/N/O/P/Q(process_row).
|
|
- 부모판정: leaf라벨이 자기 카테고리에 없음(self-referential 제외) AND 직후행이 이미 카테고리 아님.
|
|
- 카테고리행: leaf+1열, process_row로 L/M/N/O/P/Q.
|
|
- 기존 관광지 5행도 O/P/Q 채움.
|
|
- bottom-up 처리(삽입이 상위행 위치 안흔듦) + 병합재매핑 + 하이퍼링크 전체재구성.
|
|
사용: python -X utf8 _서산_카테고리전개.py [--write]
|
|
"""
|
|
import sys, os, shutil, importlib.util
|
|
from urllib.parse import urljoin, urlsplit, parse_qsl, urlencode, urlunsplit
|
|
import openpyxl
|
|
from openpyxl.worksheet.hyperlink import Hyperlink
|
|
|
|
def canon(u):
|
|
sp=urlsplit(u)
|
|
q=[(k,v) for k,v in parse_qsl(sp.query,keep_blank_values=True) if v]
|
|
return urlunsplit((sp.scheme,sp.netloc,sp.path,urlencode(sorted(q)),''))
|
|
|
|
HERE=os.path.dirname(os.path.abspath(__file__))
|
|
s=importlib.util.spec_from_file_location('m',os.path.join(HERE,'_chungnam_phase234_all.py'))
|
|
m=importlib.util.module_from_spec(s); s.loader.exec_module(m)
|
|
XLSX=r'D:\01.프로젝트\DB수집\작업파일\광역_사이트맵\충청남도\8.서산시\충청남도_서산시.xlsx'
|
|
BODY=['#contents','#txt','main']
|
|
|
|
def leaf_col(ws,r):
|
|
for c in range(10,3,-1):
|
|
if ws.cell(r,c).value not in (None,''): return c
|
|
return 4
|
|
|
|
def get_cats(K):
|
|
soup=m.fetch(K)
|
|
if soup is None: return None
|
|
div=soup.select_one('div.bbs_category_list')
|
|
if not div: return None
|
|
out=[]
|
|
for a in div.find_all('a',href=True):
|
|
t=(a.get_text() or '').strip()
|
|
if t: out.append((t,urljoin(K,a['href'])))
|
|
return out or None # 전체 포함 전체목록(self판정용). 전개때 부모동일URL 제외.
|
|
|
|
def find_parents(ws):
|
|
res=[]
|
|
for r in range(3,ws.max_row+1):
|
|
if ws.cell(r,12).value!='게시판': continue
|
|
K=ws.cell(r,11).value
|
|
if not(isinstance(K,str) and K.startswith('http')): continue
|
|
lc=leaf_col(ws,r); lab=ws.cell(r,lc).value
|
|
cats=get_cats(K)
|
|
if not cats: continue
|
|
cl=[t for t,_ in cats]
|
|
if lab in cl: continue # 이미 카테고리행(self)
|
|
if ws.cell(r+1,lc+1).value==cl[0]: continue # 이미 전개됨
|
|
res.append((r,lab,lc,cats))
|
|
return res
|
|
|
|
def remap_merges(ws, ins, n):
|
|
ranges=[(x.min_col,x.min_row,x.max_col,x.max_row) for x in list(ws.merged_cells.ranges)]
|
|
for x in list(ws.merged_cells.ranges): ws.unmerge_cells(str(x))
|
|
ws.insert_rows(ins,n)
|
|
for mc,mr,xc,xr in ranges:
|
|
if mr>=ins: mr+=n; xr+=n
|
|
elif xr>=ins: xr+=n
|
|
ws.merge_cells(start_row=mr,start_column=mc,end_row=xr,end_column=xc)
|
|
|
|
def main():
|
|
write='--write' in sys.argv
|
|
wb=openpyxl.load_workbook(XLSX); ws=wb.active
|
|
C=None
|
|
for r in range(3,ws.max_row+1):
|
|
if ws.cell(r,3).value: C=ws.cell(r,3).value; break
|
|
parents=find_parents(ws)
|
|
parents.sort(key=lambda t:-t[0]) # bottom-up
|
|
print('전개대상 부모보드:',[(lab,len(cats)) for r,lab,lc,cats in sorted(parents,key=lambda t:t[0])])
|
|
if not write:
|
|
for r,lab,lc,cats in sorted(parents,key=lambda t:t[0]):
|
|
print(' r%d %s(leafcol%d) → %s'%(r,lab,lc,[t for t,_ in cats]))
|
|
return
|
|
shutil.copy(XLSX, XLSX.replace('.xlsx','_backup_카테고리전개전.xlsx'))
|
|
total_new=0
|
|
for r,lab,lc,cats in parents: # descending
|
|
pk=canon(ws.cell(r,11).value)
|
|
cats=[(t,u) for t,u in cats if canon(u)!=pk] # 전체(부모동일URL) 제외
|
|
n=len(cats); ins=r+1
|
|
if n==0: continue
|
|
remap_merges(ws, ins, n)
|
|
for i,(t,url) in enumerate(cats):
|
|
rr=ins+i
|
|
out=m.process_row(url, BODY)
|
|
ws.cell(rr,3).value=C
|
|
ws.cell(rr,lc+1).value=t
|
|
ws.cell(rr,11).value=url
|
|
ws.cell(rr,12).value=out['L'] or '게시판'
|
|
ws.cell(rr,13).value=out['M'] if out['M']!='' else 0
|
|
ws.cell(rr,14).value=out['N']
|
|
ws.cell(rr,15).value=out['O']; ws.cell(rr,16).value=out['P']; ws.cell(rr,17).value=out['Q']
|
|
ws.row_dimensions[rr].height=15
|
|
ws.merge_cells(start_row=r,start_column=lc,end_row=r+n,end_column=lc) # 부모 leaf 병합
|
|
total_new+=n
|
|
print(' r%d %s +%d행'%(r,lab,n))
|
|
# 기존 관광지 5행 O/P/Q 채움 (searchCtgry+bbsNo=617)
|
|
fixed=0
|
|
for r in range(3,ws.max_row+1):
|
|
K=ws.cell(r,11).value
|
|
if isinstance(K,str) and 'bbsNo=617' in K and 'searchCtgry=' in K and ws.cell(r,8).value:
|
|
out=m.process_row(K, BODY)
|
|
ws.cell(r,14).value=out['N']
|
|
ws.cell(r,15).value=out['O']; ws.cell(r,16).value=out['P']; ws.cell(r,17).value=out['Q']
|
|
fixed+=1
|
|
# 하이퍼링크 전체 재구성
|
|
for r in range(3,ws.max_row+1):
|
|
v=ws.cell(r,11).value; cell=ws.cell(r,11)
|
|
if isinstance(v,str) and v.startswith('http'): cell.hyperlink=Hyperlink(ref=cell.coordinate,target=v)
|
|
else: cell.hyperlink=None
|
|
wb.save(XLSX)
|
|
print(f'적용: 신규 {total_new}행 · 관광지 O/P/Q채움 {fixed}행 · 하이퍼링크 재구성 · 백업')
|
|
|
|
if __name__=='__main__': main()
|