# -*- coding: utf-8 -*- """부여군 사전정보공개(정보목록공개) fiexd_tab1 scate=1~9 분야탭 전개. - _tab_expand 의 일반 필터탭 제외 규칙(같은 경로·쿼리만 다름)에 걸려 누락된 케이스. 사용자 지시로 9개 분야탭을 각각 별도 게시판 행으로 전개(보령시 사전정보공표 컨벤션). - 각 분야 = 게시판 / M=목록 항목수(테이블 데이터행) / N=어문 / O=미부착. - 평탄화→치환→재구성(D/E/F 재병합·K하이퍼링크·B순번)은 _tab_expand 재사용. """ import sys, shutil, warnings, re from copy import copy import requests, openpyxl from bs4 import BeautifulSoup from openpyxl.styles import Alignment, Font from openpyxl.utils import get_column_letter sys.path.insert(0, r'D:\01.프로젝트\DB수집\_스크립트') from _tab_expand import load_flat, MAXCOL, CAT_COLS, HEADER_MERGES warnings.filterwarnings('ignore') H = {'User-Agent': 'Mozilla/5.0'} XLSX = r'D:\01.프로젝트\DB수집\작업파일\광역_사이트맵\충청남도\7.부여군\충청남도_부여군.xlsx' BASE = 'https://www.buyeo.go.kr/_prog/service/index.php?scate=' def collect(): """9개 scate 라벨+항목수 수집.""" r = requests.get(BASE + '1', headers=H, timeout=15, verify=False) soup = BeautifulSoup(r.content, 'html.parser') labels = {} for li in soup.select('.fiexd_tab1 li'): a = li.find('a'); m = re.search(r'scate=(\d+)', a.get('href') or '') if m: labels[int(m.group(1))] = a.get_text(strip=True) out = [] for sc in sorted(labels): rr = requests.get(BASE + str(sc), headers=H, timeout=15, verify=False) s = BeautifulSoup(rr.content, 'html.parser') t = s.find('table') cnt = sum(1 for tr in t.find_all('tr') if tr.find_all('td')) out.append((sc, labels[sc], cnt)) return out def main(): write = '--write' in sys.argv scates = collect() print('=== scate 분야탭 ===') for sc, lab, cnt in scates: print(f' scate={sc} | 항목={cnt} | {lab}') print(f'합계 항목 {sum(c for _,_,c in scates)} / 9행 전개\n') wb = openpyxl.load_workbook(XLSX) ws = wb.active rows = load_flat(ws) # 대상 행 식별: K가 scate=1 tgt = None for row in rows: k = row['vals'].get(11) if isinstance(k, str) and 'service/index.php?scate=1' in k: tgt = row; break if tgt is None: print('!! 대상(scate=1) 행 없음'); return lc = 6 # leaf=F (사전정보공개) childc = lc + 1 # G print(f'대상 sheet행 {tgt["src"]} (F={tgt["vals"].get(6)}) → 자식 {get_column_letter(childc)} 에 9분야 전개') if not write: print('\n(계획만. --write 로 기입)') return backup = XLSX.replace('.xlsx', '_backup_scate전.xlsx') shutil.copy(XLSX, backup) print('백업:', backup) out_rows = [] for row in rows: if row is tgt: for sc, lab, cnt in scates: nv = dict(tgt['vals']) for c in CAT_COLS: if c > lc: nv[c] = None nv[childc] = lab nv[11] = BASE + str(sc) nv[12] = '게시판' nv[13] = cnt nv[14] = '어문' nv[15] = '미부착' for c in (16, 17, 18): # P,Q,R 비움 nv[c] = None # 신규 행(scate>1)은 비고~ 비움, scate=1은 기존 보존 if sc != 1: for c in range(19, MAXCOL + 1): nv[c] = None out_rows.append({'vals': nv, 'style': tgt, 'url': nv[11]}) else: out_rows.append({'vals': dict(row['vals']), 'style': row, 'url': row['vals'].get(11)}) # 데이터 클리어 for r in range(3, ws.max_row + 1): for c in range(1, MAXCOL + 1): ws.cell(r, c).value = None ws.cell(r, c).hyperlink = None START = 3 for i, orow in enumerate(out_rows): r = START + i sty = orow['style']['styles'] for c in range(1, MAXCOL + 1): cell = ws.cell(r, c) f, fl, bd, al, nf, pr = sty[c] cell.font = copy(f); cell.fill = copy(fl); cell.border = copy(bd) cell.alignment = copy(al); cell.number_format = nf; cell.protection = copy(pr) v = orow['vals'] for c in range(1, MAXCOL + 1): ws.cell(r, c).value = v.get(c) ws.cell(r, 2).value = i + 1 END = START + len(out_rows) - 1 center = Alignment(horizontal='center', vertical='center', wrap_text=True) left = Alignment(horizontal='left', vertical='center', wrap_text=False) def merge_runs(col_letter, col_idx, group_cols=()): cur_val = ws.cell(START, col_idx).value cur_grp = tuple(ws.cell(START, g).value for g in group_cols) run_start = START; runs = [] for r in range(START + 1, END + 1): val = ws.cell(r, col_idx).value grp = tuple(ws.cell(r, gg).value for gg in group_cols) if val == cur_val and grp == cur_grp: continue if cur_val not in (None, '') and r - 1 > run_start: runs.append((run_start, r - 1)) cur_val, cur_grp, run_start = val, grp, r if cur_val not in (None, '') and END > run_start: runs.append((run_start, END)) for s, e in runs: ws.merge_cells(f'{col_letter}{s}:{col_letter}{e}') ws.cell(s, col_idx).alignment = center merge_runs('F', 6, group_cols=(4, 5)) merge_runs('E', 5, group_cols=(4,)) merge_runs('D', 4) for r in range(START, END + 1): for c in (4, 5, 6, 7): if ws.cell(r, c).value is not None: ws.cell(r, c).alignment = center for r in range(1, END + 1): ws.row_dimensions[r].height = 15 for r in range(START, END + 1): cell = ws.cell(r, 11) u = cell.value if isinstance(u, str) and u.startswith(('http://', 'https://')): cell.hyperlink = u old = cell.font cell.font = Font(name=old.name or '맑은 고딕', size=old.size or 11, bold=old.bold, italic=old.italic, color='0000FF', underline='single') cell.alignment = left wb.save(XLSX) print(f'저장 완료: {XLSX} (총 {len(out_rows)}행)') if __name__ == '__main__': main()