Written on
·
13
0
한글파일명인 액셀파일을 첨부하는 것은 안되나요?
아니면, 액셀 내 columns value 값이 한글인 것을 가져와서 메일 헤더나 파일명으로 사용한게 에러인 것인지..
궁금합니다...
메일주소와 password 는 '^^^^^' 임의 처리한 코딩입니다.
오류내용:
❌ 이메일 발송 실패: 진리종합상사 - 'ascii' codec can't encode characters in position 5-7: ordinal not in range(128)
import pandas as pd
import os
from openpyxl.utils import get_column_letter
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
import smtplib
from email.message import EmailMessage
import ssl
from email.header import Header
from email.utils import formataddr
# -------------------------
# 설정
# -------------------------
cost_manage_path = 'cost_manage.xlsx'
partner_path = 'partner.xlsx'
output_dir = 'output'
SENDER_EMAIL = '^^^^^^^^^^^^^^'
SENDER_PASSWORD = '^^^^^^^^^^^^^^'
SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 587
# -------------------------
# 엑셀 읽기
# -------------------------
try:
cost_data_df = pd.read_excel(cost_manage_path, sheet_name='Sheet1', header=1)
partner_sheet1_df = pd.read_excel(partner_path, sheet_name='Sheet1')
partner_sheet2_df = pd.read_excel(partner_path, sheet_name='Sheet2')
except FileNotFoundError as e:
print(f"오류: 파일을 찾을 수 없습니다. {e.filename}")
exit()
# -------------------------
# 데이터 전처리
# -------------------------
for df in [cost_data_df, partner_sheet1_df, partner_sheet2_df]:
df.columns = df.columns.str.strip()
cost_data_df = cost_data_df.drop(columns=['협력사ID', '협력사명'])
cost_data_df['상세분류ID'] = cost_data_df['상세분류ID'].astype(str).str.strip()
partner_sheet1_df['상세분류ID'] = partner_sheet1_df['상세분류ID'].astype(str).str.strip()
partner_sheet2_df['협력사KEY'] = partner_sheet2_df['협력사KEY'].astype(str).str.strip()
merged_df = pd.merge(cost_data_df, partner_sheet1_df, on='상세분류ID', how='left')
final_df = pd.merge(merged_df, partner_sheet2_df, on='협력사KEY', how='left')
final_df = final_df.drop(columns=['상세분류명_y'])
final_df = final_df.rename(columns={'상세분류명_x': '상세분류명'})
final_columns = ['상세분류ID', '상세분류명', '협력사KEY', '협력사ID', '협력사명',
'상품ID', '규격', '단가', 'moq', '이메일주소']
real_final_df = final_df[final_columns]
grouped_by_partner = real_final_df.groupby('협력사KEY')
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# -------------------------
# 엑셀 생성 & 이메일 발송
# -------------------------
for partner_key, group_df in grouped_by_partner:
# 엑셀 데이터 준비
columns_for_excel = ['상세분류ID', '상세분류명', '협력사ID', '협력사명', '상품ID', '규격', '단가', 'moq']
data_to_write = group_df[columns_for_excel].copy()
header_row1 = pd.DataFrame([['', '', '', '', '', '', '▼작성', '▼작성']], columns=columns_for_excel)
header_row2 = pd.DataFrame([columns_for_excel], columns=columns_for_excel)
final_excel_df = pd.concat([header_row1, header_row2, data_to_write], ignore_index=True)
# 파일 경로
file_name = f"{partner_key}_견적_요청서.xlsx"
output_path = os.path.join(output_dir, file_name)
# 엑셀 생성
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
final_excel_df.to_excel(writer, index=False, header=False, sheet_name='Sheet1')
worksheet = writer.sheets['Sheet1']
worksheet.sheet_view.showGridLines = False
# 스타일 설정
bold_yellow_fill = PatternFill(start_color='FFFF00', end_color='FFFF00', fill_type='solid')
center_align = Alignment(horizontal='center')
left_align = Alignment(horizontal='left')
bold_font = Font(bold=True)
thin_border = Border(left=Side(style='thin'), right=Side(style='thin'),
top=Side(style='thin'), bottom=Side(style='thin'))
# 1행 스타일
for cell in worksheet[1]:
if cell.value == '▼작성':
cell.fill = bold_yellow_fill
cell.font = bold_font
# 2행 스타일
for cell in worksheet[2]:
cell.alignment = center_align
cell.border = thin_border
# 데이터 행 스타일
left_align_cols = ['상세분류ID', '상세분류명', '규격']
col_map = {col: i + 1 for i, col in enumerate(columns_for_excel)}
for row_num in range(3, worksheet.max_row + 1):
for col_name in columns_for_excel:
cell = worksheet.cell(row=row_num, column=col_map[col_name])
cell.alignment = left_align if col_name in left_align_cols else center_align
cell.border = thin_border
# 열 너비 자동 조정
for column in worksheet.columns:
max_length = 0
column_letter = get_column_letter(column[0].column)
for cell in column:
if cell.value:
max_length = max(max_length, len(str(cell.value)))
worksheet.column_dimensions[column_letter].width = max_length + (20 if column_letter == get_column_letter(col_map['규격']) else 5)
print(f"✅ '{file_name}' 파일이 '{output_dir}' 폴더에 생성되었습니다.")
# 이메일 정보
partner_email = group_df['이메일주소'].iloc[0]
partner_name = group_df['협력사명'].iloc[0]
if pd.isna(partner_email):
print(f"❌ {partner_name}의 이메일 주소가 없어 이메일 발송을 건너뜁니다.")
continue
print(f"📧 '{partner_name}' ({partner_email})님에게 이메일 발송 중...")
msg = EmailMessage()
msg['From'] = formataddr((str(Header('보내는 사람 이름', 'utf-8')), SENDER_EMAIL))
msg['To'] = partner_email
# 이 부분만 이전 코드로 되돌려 일반 문자열을 할당합니다.
msg['Subject'] = f'[{partner_name}] 월간 비용 견적 요청서입니다.'
body = f"""안녕하세요, {partner_name} 담당자님.
월간 비용 견적 요청서(첨부파일)를 보내드립니다.
확인 후 회신 부탁드립니다.
감사합니다.
--
보내는 사람 이름
"""
msg.set_content(body)
try:
with open(output_path, 'rb') as f:
# `filename` 인수를 일반 문자열로 전달
msg.add_attachment(f.read(),
maintype='application',
subtype='octet-stream',
filename=file_name)
except Exception as e:
print(f"❌ 파일 첨부 중 오류 발생: {e}")
continue
try:
context = ssl.create_default_context()
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
server.starttls(context=context)
server.login(SENDER_EMAIL, SENDER_PASSWORD)
server.send_message(msg)
print(f"✅ 이메일 발송 성공: {partner_name}")
except Exception as e:
print(f"❌ 이메일 발송 실패: {partner_name} - {e}")
Answer