묻고 답해요
160만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결Python 엑셀 프로그래밍 - with xlsxwriter
메일보내기 할 때 에러..
한글파일명인 액셀파일을 첨부하는 것은 안되나요?아니면, 액셀 내 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}")
-
미해결Python 엑셀 프로그래밍 - with xlsxwriter
강의자료 어디서 확인하나요
코딩내용 어디서 다운로드 받을 수 있나요? 질문&답변 란 보는데도 도저히 못찾겠네요..
-
미해결Python 엑셀 프로그래밍 - with xlsxwriter
강의 자료 위치
강의 중간중간에 강의 jupyter notebook을 공유해 주신다고 하시는데 자료는 어디서 받을 수 있는건가요?"PDF 파일 다루기" 강의를 듣고있는데 공유해주신 notebook 참고해서 사용하라고 하는데, 공유해주신 파일이 어디에 있는지 알 수 없습니다.