공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
167 lines
6.5 KiB
Python
167 lines
6.5 KiB
Python
# -*- 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()
|