강의

멘토링

로드맵

Inflearn Community Q&A

mihee7392's profile image
mihee7392

asked

Python Excel Programming - with xlsxwriter

Send mail

메일보내기 할 때 에러..

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}")

 

pythonxlsxwriter

Answer

This question is waiting for answers
Be the first to answer!
mihee7392's profile image
mihee7392

asked

Ask a question