Python 자동화 마스터 5편: 엑셀과 CSV 파일 자동화
Python Automation Master Part 5: Excel and CSV File Automation
서론: 스프레드시트 자동화의 중요성
엑셀과 CSV 파일은 비즈니스 환경에서 가장 널리 사용되는 데이터 형식입니다. 매일 수많은 데이터가 스프레드시트 형태로 생성되고, 처리되고, 보고됩니다. 이러한 반복적인 작업을 Python으로 자동화하면 시간을 절약하고 인적 오류를 줄일 수 있습니다.
이번 5편에서는 Python의 csv 모듈, pandas, openpyxl 라이브러리를 활용하여 CSV와 엑셀 파일을 효율적으로 처리하는 방법을 배웁니다. 기본적인 파일 읽기/쓰기부터 셀 스타일링, 차트 생성, 대용량 파일 처리까지 실무에서 바로 활용할 수 있는 기술들을 다룹니다.
1. CSV 파일 처리 기초 (csv 모듈)
Python의 내장 csv 모듈은 별도의 설치 없이 CSV 파일을 처리할 수 있게 해줍니다.
1.1 CSV 파일 읽기
import csv
# 기본 CSV 읽기
with open('data.csv', 'r', encoding='utf-8') as file:
reader = csv.reader(file)
for row in reader:
print(row) # 각 행은 리스트로 반환됨
# 딕셔너리로 읽기 (헤더 기반)
with open('data.csv', 'r', encoding='utf-8') as file:
reader = csv.DictReader(file)
for row in reader:
print(row['이름'], row['나이']) # 열 이름으로 접근
# 구분자 지정
with open('data.tsv', 'r', encoding='utf-8') as file:
reader = csv.reader(file, delimiter='\t') # 탭 구분
for row in reader:
print(row)
1.2 CSV 파일 쓰기
import csv
# 기본 CSV 쓰기
data = [
['이름', '나이', '도시'],
['김철수', 25, '서울'],
['이영희', 30, '부산'],
['박민수', 28, '대구']
]
with open('output.csv', 'w', encoding='utf-8-sig', newline='') as file:
writer = csv.writer(file)
writer.writerows(data) # 여러 행 한 번에 쓰기
# 딕셔너리로 쓰기
data_dict = [
{'이름': '김철수', '나이': 25, '도시': '서울'},
{'이름': '이영희', '나이': 30, '도시': '부산'},
{'이름': '박민수', '나이': 28, '도시': '대구'}
]
with open('output_dict.csv', 'w', encoding='utf-8-sig', newline='') as file:
fieldnames = ['이름', '나이', '도시']
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader() # 헤더 쓰기
writer.writerows(data_dict)
Tip: Windows에서 엑셀로 CSV 파일을 열 때 한글이 깨지는 것을 방지하려면 encoding='utf-8-sig'를 사용하세요. BOM(Byte Order Mark)이 추가되어 엑셀에서 올바르게 인식됩니다.
2. pandas로 CSV 다루기
pandas는 데이터 분석에 최적화된 라이브러리로, CSV 파일 처리에 매우 강력한 기능을 제공합니다.
2.1 pandas 설치 및 기본 사용
# 설치
pip install pandas
import pandas as pd
# CSV 읽기
df = pd.read_csv('data.csv', encoding='utf-8')
# 기본 정보 확인
print(df.head()) # 처음 5행
print(df.info()) # 데이터 타입 및 결측치 정보
print(df.describe()) # 통계 요약
# 특정 열 선택
print(df['이름']) # 단일 열
print(df[['이름', '나이']]) # 여러 열
# 조건 필터링
adults = df[df['나이'] >= 20]
seoul_people = df[df['도시'] == '서울']
2.2 데이터 전처리 및 가공
import pandas as pd
df = pd.read_csv('sales_data.csv')
# 결측치 처리
df.fillna(0, inplace=True) # 0으로 채우기
df.dropna(subset=['필수열'], inplace=True) # 특정 열의 결측치 행 삭제
# 새 열 추가
df['총액'] = df['단가'] * df['수량']
df['할인가'] = df['총액'] * 0.9
# 열 이름 변경
df.rename(columns={'old_name': 'new_name'}, inplace=True)
# 데이터 정렬
df.sort_values(by='총액', ascending=False, inplace=True)
# 그룹별 집계
summary = df.groupby('카테고리').agg({
'총액': 'sum',
'수량': 'mean',
'주문번호': 'count'
}).rename(columns={'주문번호': '주문수'})
# CSV로 저장
df.to_csv('processed_data.csv', index=False, encoding='utf-8-sig')
2.3 여러 CSV 파일 병합
import pandas as pd
import glob
# 같은 형식의 여러 CSV 파일 병합
csv_files = glob.glob('data/*.csv')
df_list = [pd.read_csv(f) for f in csv_files]
combined_df = pd.concat(df_list, ignore_index=True)
# 두 데이터프레임 병합 (JOIN)
df1 = pd.read_csv('customers.csv')
df2 = pd.read_csv('orders.csv')
# 내부 조인 (양쪽에 있는 것만)
merged = pd.merge(df1, df2, on='고객ID', how='inner')
# 외부 조인 (모든 데이터 포함)
merged_all = pd.merge(df1, df2, on='고객ID', how='outer')
3. openpyxl로 엑셀 파일 생성/수정
openpyxl은 엑셀 파일(.xlsx)을 생성하고 수정하는 데 특화된 라이브러리입니다.
3.1 설치 및 기본 사용
# 설치
pip install openpyxl
from openpyxl import Workbook, load_workbook
# 새 워크북 생성
wb = Workbook()
ws = wb.active
ws.title = "판매 데이터"
# 데이터 입력
ws['A1'] = '제품명'
ws['B1'] = '수량'
ws['C1'] = '단가'
# 여러 행 추가
data = [
['노트북', 10, 1200000],
['마우스', 50, 25000],
['키보드', 30, 45000]
]
for row in data:
ws.append(row)
# 저장
wb.save('sales.xlsx')
# 기존 파일 열기
wb = load_workbook('sales.xlsx')
ws = wb.active
# 셀 값 읽기
print(ws['A1'].value)
print(ws.cell(row=2, column=1).value)
# 범위 읽기
for row in ws.iter_rows(min_row=2, max_row=4, min_col=1, max_col=3):
for cell in row:
print(cell.value, end=' ')
print()
3.2 셀 스타일링 (폰트, 색상, 테두리)
from openpyxl import Workbook
from openpyxl.styles import Font, Fill, PatternFill, Border, Side, Alignment
wb = Workbook()
ws = wb.active
# 데이터 입력
headers = ['제품명', '수량', '단가', '총액']
for col, header in enumerate(headers, 1):
ws.cell(row=1, column=col, value=header)
# 폰트 스타일
bold_font = Font(bold=True, size=12, color='FFFFFF')
normal_font = Font(size=11)
# 배경색
header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
alt_fill = PatternFill(start_color='D9E2F3', end_color='D9E2F3', fill_type='solid')
# 테두리
thin_border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# 정렬
center_align = Alignment(horizontal='center', vertical='center')
right_align = Alignment(horizontal='right')
# 헤더에 스타일 적용
for col in range(1, 5):
cell = ws.cell(row=1, column=col)
cell.font = bold_font
cell.fill = header_fill
cell.border = thin_border
cell.alignment = center_align
# 데이터 행 스타일
data = [
['노트북', 10, 1200000],
['마우스', 50, 25000],
['키보드', 30, 45000]
]
for row_idx, row_data in enumerate(data, 2):
for col_idx, value in enumerate(row_data, 1):
cell = ws.cell(row=row_idx, column=col_idx, value=value)
cell.border = thin_border
if row_idx % 2 == 0:
cell.fill = alt_fill
# 열 너비 조정
ws.column_dimensions['A'].width = 15
ws.column_dimensions['B'].width = 10
ws.column_dimensions['C'].width = 15
ws.column_dimensions['D'].width = 15
# 행 높이 조정
ws.row_dimensions[1].height = 25
wb.save('styled_sales.xlsx')
3.3 수식 삽입
from openpyxl import Workbook
wb = Workbook()
ws = wb.active
# 데이터 입력
ws['A1'] = '수량'
ws['B1'] = '단가'
ws['C1'] = '총액'
data = [
[10, 1200000],
[50, 25000],
[30, 45000]
]
for row_idx, (qty, price) in enumerate(data, 2):
ws.cell(row=row_idx, column=1, value=qty)
ws.cell(row=row_idx, column=2, value=price)
# 수식 삽입 (총액 = 수량 * 단가)
ws.cell(row=row_idx, column=3, value=f'=A{row_idx}*B{row_idx}')
# 합계 수식
last_row = len(data) + 1
ws.cell(row=last_row + 1, column=1, value='합계')
ws.cell(row=last_row + 1, column=3, value=f'=SUM(C2:C{last_row})')
# 조건부 수식
ws['D1'] = '등급'
for row_idx in range(2, last_row + 1):
ws.cell(row=row_idx, column=4,
value=f'=IF(C{row_idx}>=10000000,"A",IF(C{row_idx}>=1000000,"B","C"))')
wb.save('formulas.xlsx')
4. 차트 생성
openpyxl을 사용하면 엑셀에서 다양한 차트를 프로그래밍 방식으로 생성할 수 있습니다.
from openpyxl import Workbook
from openpyxl.chart import BarChart, LineChart, PieChart, Reference
wb = Workbook()
ws = wb.active
# 데이터 입력
data = [
['월', '매출', '비용', '이익'],
['1월', 5000000, 3000000, 2000000],
['2월', 6000000, 3500000, 2500000],
['3월', 5500000, 3200000, 2300000],
['4월', 7000000, 4000000, 3000000],
['5월', 8000000, 4500000, 3500000],
['6월', 7500000, 4200000, 3300000]
]
for row in data:
ws.append(row)
# 막대 차트
bar_chart = BarChart()
bar_chart.type = "col" # 세로 막대
bar_chart.style = 10
bar_chart.title = "월별 매출 현황"
bar_chart.y_axis.title = "금액 (원)"
bar_chart.x_axis.title = "월"
# 데이터 범위 지정
data_ref = Reference(ws, min_col=2, min_row=1, max_col=4, max_row=7)
categories = Reference(ws, min_col=1, min_row=2, max_row=7)
bar_chart.add_data(data_ref, titles_from_data=True)
bar_chart.set_categories(categories)
bar_chart.shape = 4 # 차트 스타일
ws.add_chart(bar_chart, "F2") # 차트 위치
# 꺾은선 차트
line_chart = LineChart()
line_chart.title = "월별 추이"
line_chart.style = 13
line_chart.y_axis.title = "금액"
line_chart.x_axis.title = "월"
line_chart.add_data(data_ref, titles_from_data=True)
line_chart.set_categories(categories)
ws.add_chart(line_chart, "F18")
# 원형 차트 (6월 데이터만)
pie_chart = PieChart()
pie_chart.title = "6월 비용 구조"
labels = Reference(ws, min_col=2, max_col=4, min_row=1)
pie_data = Reference(ws, min_col=2, max_col=4, min_row=7)
pie_chart.add_data(pie_data, titles_from_data=False)
pie_chart.set_categories(labels)
ws.add_chart(pie_chart, "N2")
wb.save('charts.xlsx')
5. 여러 시트 다루기
from openpyxl import Workbook, load_workbook
# 새 워크북에 여러 시트 생성
wb = Workbook()
# 첫 번째 시트 (기본 시트)
ws1 = wb.active
ws1.title = "요약"
# 추가 시트 생성
ws2 = wb.create_sheet("1분기")
ws3 = wb.create_sheet("2분기")
ws4 = wb.create_sheet("3분기", 0) # 맨 앞에 삽입
# 시트별 데이터 입력
quarters = {
'1분기': [['1월', 100], ['2월', 120], ['3월', 110]],
'2분기': [['4월', 130], ['5월', 140], ['6월', 135]],
'3분기': [['7월', 150], ['8월', 160], ['9월', 155]]
}
for sheet_name, data in quarters.items():
ws = wb[sheet_name]
ws.append(['월', '매출'])
for row in data:
ws.append(row)
# 요약 시트에 각 분기 합계 참조
summary = wb['요약']
summary.append(['분기', '총매출'])
summary.append(['1분기', '=SUM(\'1분기\'!B2:B4)'])
summary.append(['2분기', '=SUM(\'2분기\'!B2:B4)'])
summary.append(['3분기', '=SUM(\'3분기\'!B2:B4)'])
# 시트 순서 변경
wb._sheets = [wb['요약'], wb['1분기'], wb['2분기'], wb['3분기']]
# 시트 복사
source = wb['1분기']
target = wb.copy_worksheet(source)
target.title = "1분기_백업"
# 시트 삭제
# del wb['1분기_백업']
wb.save('multi_sheets.xlsx')
# 기존 파일의 모든 시트 순회
wb = load_workbook('multi_sheets.xlsx')
for sheet_name in wb.sheetnames:
ws = wb[sheet_name]
print(f"시트: {sheet_name}")
for row in ws.iter_rows(values_only=True):
print(row)
6. 엑셀 파일 병합
import pandas as pd
from openpyxl import load_workbook
import glob
# pandas를 사용한 엑셀 파일 병합
def merge_excel_files(file_pattern, output_file):
"""여러 엑셀 파일을 하나로 병합"""
excel_files = glob.glob(file_pattern)
df_list = []
for file in excel_files:
df = pd.read_excel(file)
df['원본파일'] = file # 출처 표시
df_list.append(df)
combined_df = pd.concat(df_list, ignore_index=True)
combined_df.to_excel(output_file, index=False)
print(f"병합 완료: {len(excel_files)}개 파일 -> {output_file}")
merge_excel_files('reports/*.xlsx', 'combined_report.xlsx')
# 여러 시트를 하나의 시트로 병합
def merge_sheets(input_file, output_file):
"""한 엑셀 파일 내 모든 시트를 하나로 병합"""
xl = pd.ExcelFile(input_file)
df_list = []
for sheet_name in xl.sheet_names:
df = pd.read_excel(xl, sheet_name=sheet_name)
df['시트명'] = sheet_name
df_list.append(df)
combined_df = pd.concat(df_list, ignore_index=True)
combined_df.to_excel(output_file, index=False)
merge_sheets('multi_sheets.xlsx', 'merged_single_sheet.xlsx')
# 조건부 병합 (특정 열 기준)
def conditional_merge(files, key_column, output_file):
"""특정 키 열을 기준으로 여러 파일 병합"""
base_df = pd.read_excel(files[0])
for file in files[1:]:
df = pd.read_excel(file)
base_df = pd.merge(base_df, df, on=key_column, how='outer')
base_df.to_excel(output_file, index=False)
conditional_merge(['customers.xlsx', 'orders.xlsx', 'payments.xlsx'],
'고객ID', 'customer_full_data.xlsx')
7. 대용량 파일 처리 (Chunking)
수십만 행 이상의 대용량 파일을 처리할 때는 메모리 효율을 고려해야 합니다.
import pandas as pd
from openpyxl import load_workbook
from openpyxl.utils.dataframe import dataframe_to_rows
# 청크 단위로 대용량 CSV 처리
def process_large_csv(input_file, output_file, chunk_size=10000):
"""대용량 CSV를 청크 단위로 처리"""
processed_chunks = []
for chunk in pd.read_csv(input_file, chunksize=chunk_size):
# 각 청크별 처리
chunk['처리됨'] = True
chunk['총액'] = chunk['수량'] * chunk['단가']
processed_chunks.append(chunk)
print(f"처리된 행: {len(chunk)}")
# 결과 저장
result_df = pd.concat(processed_chunks, ignore_index=True)
result_df.to_csv(output_file, index=False)
# 청크별로 필터링하여 저장 (메모리 절약)
def filter_large_csv(input_file, output_file, condition_func, chunk_size=10000):
"""대용량 CSV에서 조건에 맞는 행만 추출"""
first_chunk = True
for chunk in pd.read_csv(input_file, chunksize=chunk_size):
filtered = chunk[condition_func(chunk)]
if first_chunk:
filtered.to_csv(output_file, index=False, mode='w')
first_chunk = False
else:
filtered.to_csv(output_file, index=False, mode='a', header=False)
# 사용 예시: 총액이 100만원 이상인 행만 추출
filter_large_csv(
'large_sales.csv',
'high_value_sales.csv',
lambda df: df['총액'] >= 1000000
)
# 대용량 엑셀 파일 읽기 (read_only 모드)
def read_large_excel(file_path):
"""대용량 엑셀 파일을 메모리 효율적으로 읽기"""
wb = load_workbook(file_path, read_only=True)
ws = wb.active
for row in ws.iter_rows(values_only=True):
yield row # 제너레이터로 한 행씩 반환
wb.close()
# 대용량 엑셀 파일 쓰기 (write_only 모드)
from openpyxl import Workbook
def write_large_excel(data_generator, output_file):
"""대용량 데이터를 엑셀로 효율적으로 쓰기"""
wb = Workbook(write_only=True)
ws = wb.create_sheet()
for row in data_generator:
ws.append(row)
wb.save(output_file)
8. 실전: 월별 리포트 자동 생성
이제 배운 내용을 종합하여 실무에서 사용할 수 있는 월별 판매 리포트 자동 생성 시스템을 만들어 보겠습니다.
import pandas as pd
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Border, Side, Alignment
from openpyxl.chart import BarChart, LineChart, PieChart, Reference
from openpyxl.utils.dataframe import dataframe_to_rows
from datetime import datetime
import os
class MonthlyReportGenerator:
"""월별 판매 리포트 자동 생성 클래스"""
def __init__(self, data_file, output_dir='reports'):
self.data_file = data_file
self.output_dir = output_dir
self.df = None
self.wb = None
# 출력 디렉토리 생성
os.makedirs(output_dir, exist_ok=True)
# 스타일 정의
self.header_font = Font(bold=True, size=11, color='FFFFFF')
self.header_fill = PatternFill(start_color='2F5496', end_color='2F5496', fill_type='solid')
self.border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
self.center_align = Alignment(horizontal='center', vertical='center')
self.number_format = '#,##0'
self.currency_format = '#,##0"원"'
def load_data(self):
"""데이터 로드"""
self.df = pd.read_csv(self.data_file, parse_dates=['주문일자'])
self.df['월'] = self.df['주문일자'].dt.to_period('M')
print(f"데이터 로드 완료: {len(self.df)}행")
def create_summary_sheet(self, ws):
"""요약 시트 생성"""
ws.title = "요약"
# 제목
ws['A1'] = '월별 판매 리포트'
ws['A1'].font = Font(bold=True, size=16)
ws.merge_cells('A1:E1')
# 생성 일시
ws['A2'] = f'생성일시: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}'
ws['A2'].font = Font(italic=True, size=10)
# 월별 요약 데이터
monthly_summary = self.df.groupby('월').agg({
'주문번호': 'count',
'수량': 'sum',
'총액': 'sum'
}).reset_index()
monthly_summary.columns = ['월', '주문건수', '판매수량', '총매출']
# 테이블 시작 위치
start_row = 4
headers = ['월', '주문건수', '판매수량', '총매출']
# 헤더 작성
for col, header in enumerate(headers, 1):
cell = ws.cell(row=start_row, column=col, value=header)
cell.font = self.header_font
cell.fill = self.header_fill
cell.border = self.border
cell.alignment = self.center_align
# 데이터 작성
for row_idx, row in monthly_summary.iterrows():
ws.cell(row=start_row + row_idx + 1, column=1, value=str(row['월']))
ws.cell(row=start_row + row_idx + 1, column=2, value=row['주문건수'])
ws.cell(row=start_row + row_idx + 1, column=3, value=row['판매수량'])
cell = ws.cell(row=start_row + row_idx + 1, column=4, value=row['총매출'])
cell.number_format = self.currency_format
for col in range(1, 5):
ws.cell(row=start_row + row_idx + 1, column=col).border = self.border
# 합계 행
total_row = start_row + len(monthly_summary) + 1
ws.cell(row=total_row, column=1, value='합계').font = Font(bold=True)
ws.cell(row=total_row, column=2, value=f'=SUM(B{start_row+1}:B{total_row-1})')
ws.cell(row=total_row, column=3, value=f'=SUM(C{start_row+1}:C{total_row-1})')
ws.cell(row=total_row, column=4, value=f'=SUM(D{start_row+1}:D{total_row-1})')
ws.cell(row=total_row, column=4).number_format = self.currency_format
for col in range(1, 5):
ws.cell(row=total_row, column=col).border = self.border
# 열 너비 조정
ws.column_dimensions['A'].width = 12
ws.column_dimensions['B'].width = 12
ws.column_dimensions['C'].width = 12
ws.column_dimensions['D'].width = 18
# 차트 추가
chart = BarChart()
chart.type = "col"
chart.style = 10
chart.title = "월별 매출 현황"
chart.y_axis.title = "매출(원)"
data = Reference(ws, min_col=4, min_row=start_row,
max_row=start_row + len(monthly_summary))
cats = Reference(ws, min_col=1, min_row=start_row + 1,
max_row=start_row + len(monthly_summary))
chart.add_data(data, titles_from_data=True)
chart.set_categories(cats)
chart.shape = 4
ws.add_chart(chart, "F4")
return monthly_summary
def create_category_sheet(self, ws):
"""카테고리별 분석 시트 생성"""
ws.title = "카테고리별"
# 카테고리별 집계
category_summary = self.df.groupby('카테고리').agg({
'주문번호': 'count',
'수량': 'sum',
'총액': 'sum'
}).reset_index()
category_summary.columns = ['카테고리', '주문건수', '판매수량', '총매출']
category_summary = category_summary.sort_values('총매출', ascending=False)
# 테이블 작성
headers = ['카테고리', '주문건수', '판매수량', '총매출', '점유율']
for col, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col, value=header)
cell.font = self.header_font
cell.fill = self.header_fill
cell.border = self.border
total_sales = category_summary['총매출'].sum()
for row_idx, row in category_summary.reset_index(drop=True).iterrows():
ws.cell(row=row_idx + 2, column=1, value=row['카테고리'])
ws.cell(row=row_idx + 2, column=2, value=row['주문건수'])
ws.cell(row=row_idx + 2, column=3, value=row['판매수량'])
ws.cell(row=row_idx + 2, column=4, value=row['총매출']).number_format = self.currency_format
share_cell = ws.cell(row=row_idx + 2, column=5, value=row['총매출'] / total_sales)
share_cell.number_format = '0.0%'
for col in range(1, 6):
ws.cell(row=row_idx + 2, column=col).border = self.border
# 열 너비 조정
ws.column_dimensions['A'].width = 15
ws.column_dimensions['D'].width = 18
# 파이 차트
pie = PieChart()
pie.title = "카테고리별 매출 비중"
labels = Reference(ws, min_col=1, min_row=2, max_row=len(category_summary) + 1)
data = Reference(ws, min_col=4, min_row=1, max_row=len(category_summary) + 1)
pie.add_data(data, titles_from_data=True)
pie.set_categories(labels)
ws.add_chart(pie, "G2")
def create_detail_sheet(self, ws):
"""상세 데이터 시트 생성"""
ws.title = "상세데이터"
# 데이터프레임을 시트에 작성
columns = ['주문번호', '주문일자', '고객명', '제품명', '카테고리', '수량', '단가', '총액']
df_subset = self.df[columns].copy()
df_subset['주문일자'] = df_subset['주문일자'].dt.strftime('%Y-%m-%d')
# 헤더
for col, header in enumerate(columns, 1):
cell = ws.cell(row=1, column=col, value=header)
cell.font = self.header_font
cell.fill = self.header_fill
cell.border = self.border
# 데이터
for row_idx, row in df_subset.iterrows():
for col_idx, col in enumerate(columns, 1):
cell = ws.cell(row=row_idx + 2, column=col_idx, value=row[col])
cell.border = self.border
if col in ['수량', '단가', '총액']:
cell.number_format = self.number_format
# 열 너비
widths = [12, 12, 12, 20, 12, 8, 12, 15]
for col_idx, width in enumerate(widths, 1):
ws.column_dimensions[chr(64 + col_idx)].width = width
# 필터 적용
ws.auto_filter.ref = f"A1:H{len(df_subset) + 1}"
def generate_report(self):
"""리포트 생성 메인 함수"""
print("리포트 생성 시작...")
# 데이터 로드
self.load_data()
# 워크북 생성
self.wb = Workbook()
# 시트 생성
ws_summary = self.wb.active
self.create_summary_sheet(ws_summary)
ws_category = self.wb.create_sheet()
self.create_category_sheet(ws_category)
ws_detail = self.wb.create_sheet()
self.create_detail_sheet(ws_detail)
# 파일 저장
today = datetime.now().strftime('%Y%m')
output_file = os.path.join(self.output_dir, f'월별리포트_{today}.xlsx')
self.wb.save(output_file)
print(f"리포트 생성 완료: {output_file}")
return output_file
# 사용 예시
if __name__ == "__main__":
# 샘플 데이터 생성 (실제로는 기존 CSV 사용)
import random
from datetime import timedelta
# 샘플 데이터 생성
categories = ['전자제품', '의류', '식품', '가구', '도서']
products = {
'전자제품': ['노트북', '스마트폰', '태블릿', '이어폰'],
'의류': ['티셔츠', '청바지', '자켓', '코트'],
'식품': ['과자', '음료', '라면', '커피'],
'가구': ['책상', '의자', '침대', '소파'],
'도서': ['소설', '자기계발', '만화', '기술서적']
}
data = []
base_date = datetime(2025, 1, 1)
for i in range(500):
category = random.choice(categories)
product = random.choice(products[category])
qty = random.randint(1, 10)
price = random.randint(10000, 500000)
data.append({
'주문번호': f'ORD{i+1:05d}',
'주문일자': base_date + timedelta(days=random.randint(0, 180)),
'고객명': f'고객{random.randint(1, 100):03d}',
'제품명': product,
'카테고리': category,
'수량': qty,
'단가': price,
'총액': qty * price
})
sample_df = pd.DataFrame(data)
sample_df.to_csv('sample_sales_data.csv', index=False, encoding='utf-8-sig')
# 리포트 생성
generator = MonthlyReportGenerator('sample_sales_data.csv')
generator.generate_report()
마무리
이번 편에서는 Python을 활용한 엑셀과 CSV 파일 자동화의 핵심 기술들을 배웠습니다.
- csv 모듈: 간단한 CSV 파일 읽기/쓰기에 적합
- pandas: 데이터 분석과 전처리에 강력한 기능 제공
- openpyxl: 엑셀 파일 생성, 스타일링, 차트 생성에 특화
실무에서는 이 세 가지를 상황에 맞게 조합하여 사용합니다. 단순 데이터 처리는 pandas로, 최종 보고서는 openpyxl로 스타일링하는 방식이 일반적입니다.
다음 6편에서는 이메일 및 알림 자동화를 다룹니다. 오늘 만든 리포트를 자동으로 이메일로 전송하거나, 슬랙/텔레그램으로 알림을 보내는 방법을 배울 예정입니다.