공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
87 lines
2.8 KiB
Python
87 lines
2.8 KiB
Python
"""엑셀에서 사이트별 메뉴 구조 데이터 추출."""
|
|
import sys, io, json
|
|
from collections import defaultdict
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
|
|
import openpyxl
|
|
|
|
XLSX = r"D:\01.프로젝트\DB수집\2주차\공공저작물 실태조사_프리랜서_260528_장영익 검토.xlsx"
|
|
OUT = r"D:\01.프로젝트\DB수집\2주차\검토_리포트\excel_data.json"
|
|
|
|
wb = openpyxl.load_workbook(XLSX, data_only=True)
|
|
ws = wb['Sheet1']
|
|
|
|
# 컬럼 매핑 (행2 헤더 기준)
|
|
# B(2)=순번, C(3)=사이트명, D(4)=메뉴명, E(5)=카테고리(대), F(6)=중,
|
|
# G(7)=소, H(8)=소A, I(9)=소B, J(10)=소C, K(11)=URL, L(12)=게시판형태,
|
|
# M(13)=수량, N(14)=저작물유형, O(15)=공공누리부착, P(16)=마크부착위치,
|
|
# Q(17)=마크하이퍼링크
|
|
|
|
sites = defaultdict(list)
|
|
|
|
# D, E, F는 병합된 셀이라 위쪽 행에서 값 받음 — forward-fill 필요
|
|
last_D, last_E, last_F = None, None, None
|
|
last_site = None
|
|
|
|
for r in range(3, ws.max_row + 1):
|
|
seq = ws.cell(row=r, column=2).value
|
|
site = ws.cell(row=r, column=3).value
|
|
D = ws.cell(row=r, column=4).value
|
|
E = ws.cell(row=r, column=5).value
|
|
F = ws.cell(row=r, column=6).value
|
|
G = ws.cell(row=r, column=7).value
|
|
H = ws.cell(row=r, column=8).value
|
|
I = ws.cell(row=r, column=9).value
|
|
J = ws.cell(row=r, column=10).value
|
|
K = ws.cell(row=r, column=11).value
|
|
L = ws.cell(row=r, column=12).value
|
|
S = ws.cell(row=r, column=19).value
|
|
|
|
# forward-fill
|
|
if site: last_site = site
|
|
if D is not None: last_D = D
|
|
if E is not None: last_E = E
|
|
if F is not None: last_F = F
|
|
|
|
# 사이트가 바뀌면 D/E/F 캐시 리셋
|
|
if site and site != last_site:
|
|
last_D, last_E, last_F = None, None, None
|
|
last_site = site
|
|
|
|
sites[last_site or '?'].append({
|
|
'row': r,
|
|
'seq': seq,
|
|
'D': last_D if D is None else D,
|
|
'E': last_E if E is None else E,
|
|
'F': last_F if F is None else F,
|
|
'G': G,
|
|
'H': H,
|
|
'I': I,
|
|
'J': J,
|
|
'K': K,
|
|
'L': L,
|
|
'S': S,
|
|
'D_raw': D, 'E_raw': E, 'F_raw': F, # 병합 흔적 보존
|
|
})
|
|
|
|
# 사이트명에 '사이트명' 같이 헤더 흘러들어간 행 제외
|
|
filtered = {k: v for k, v in sites.items() if k and k != '사이트명'}
|
|
|
|
# datetime 등 직렬화 불가 객체 처리
|
|
def safe(x):
|
|
if x is None or isinstance(x, (str, int, float, bool)):
|
|
return x
|
|
return str(x)
|
|
|
|
cleaned = {}
|
|
for site, rows in filtered.items():
|
|
cleaned[site] = [{k: safe(v) for k, v in r.items()} for r in rows]
|
|
|
|
with open(OUT, 'w', encoding='utf-8') as f:
|
|
json.dump(cleaned, f, ensure_ascii=False, indent=2)
|
|
|
|
print('사이트별 행 수:')
|
|
for s, rows in cleaned.items():
|
|
print(f' {s}: {len(rows)}행')
|
|
print(f'\n저장: {OUT}')
|