강의

멘토링

커뮤니티

Cộng đồng Hỏi & Đáp của Inflearn

Hình ảnh hồ sơ của mihee7392
mihee7392

câu hỏi đã được viết

Lập trình Python Excel - với xlsxwriter

Gửi email

메일보내기 할 때 에러..

Viết

·

30

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

Câu trả lời

Câu hỏi này đang chờ câu trả lời
Hãy là người đầu tiên trả lời!
Hình ảnh hồ sơ của mihee7392
mihee7392

câu hỏi đã được viết

Đặt câu hỏi