ABOUT ME

-

Total
-
  • Python: VBA로 엑셀 차트 이미지 이메일에 첨부하여 보내기
    컴퓨터/파이썬 2023. 7. 25. 23:29
    728x90
    반응형

    Gmail에 이미지와 셀 데이터(A1:D10) 첨부된 상황

     

    엑셀 데이터와 차트를 정리하여 이메일로 전달해야 하는 상황을 자주 만날 수 있습니다.

    이렇게 하기 위해 메일을 작성하고, 엑셀을 열어 차트를 캡처하고, 이 캡처한 이미지를 이메일에 첨부하는 등의 일련의 과정을 수작업으로 처리하는 것은 매우 번거로울 수 있습니다.

    이번 튜토리얼에서는 파이썬과 VBA를 활용하여 이러한 과정을 자동화하는 방법을 소개합니다.

    먼저 VBA를 이용해 엑셀 차트를 PNG 이미지로 저장한 후, 파이썬을 이용해 해당 이미지와 엑셀의 데이터를 이메일에 첨부하여 전송하게 됩니다.

     

    1. 엑셀 VBA로 차트를 이미지로 저장하기


    먼저 엑셀 VBA를 이용해 엑셀 차트를 PNG 이미지로 저장하는 방법을 알아보겠습니다. 

    아래와 같은 VBA 코드를 사용하면 특정 시트의 차트를 PNG 이미지로 저장할 수 있습니다.


    Sub SaveChartAsPNG()
        Dim filePath As String
        filePath = "D:\경로\chart.png"  ' 저장할 경로를 지정하세요
        
        ThisWorkbook.Sheets("시트이름").ChartObjects(1).Chart.Export Filename:=filePath, FilterName:="PNG"
    End Sub



    이 코드를 엑셀 VBA에 적용하기 위해서는, 엑셀에서 'ALT+F11'을 눌러 VBA 편집기를 열고, 'Insert' > 'Module'을 통해 새 모듈을 생성한 뒤 위 코드를 붙여 넣으면 됩니다.

    ChartObjects(1)는 이 목록에서 첫 번째 차트를 가리킵니다.

    만약 시트에 추가된 두 번째 차트를 가리키려면 ChartObjects(2)를 사용하면 됩니다.

    즉, ChartObjects의 괄호 안에 들어가는 숫자는 차트의 순서를 나타냅니다.

    따라서 ChartObjects(1)를 사용하면 첫 번째 차트를 대상으로 작업을 수행하게 되고, 다른 숫자를 사용하면 해당 순서의 차트를 대상으로 작업을 수행하게 됩니다. 이렇게 차트의 순서를 지정하여 특정 차트를 선택할 수 있습니다.

    그러면 파이썬에서 메일 보내기 전 엑셀에서 이미지를 저장하고 알아서 불러올 것입니다.

     

    2. 파이썬으로 이메일 보내기


    이제 파이썬을 이용해 해당 이미지와 데이터를 이메일로 보내는 코드를 작성해 보겠습니다.

    이때 이메일을 보내기 위해 필요한 파이썬 라이브러리들은 다음과 같습니다:

    - 'smtplib': 이메일 서버에 접속하고 이메일을 전송하는 데 사용합니다.
    - 'email.mime': 이메일 메시지를 구성하는 데 사용합니다.
    - 'pandas': 엑셀 파일을 읽어 데이터를 처리하는 데 사용합니다.
    - 'win32com.client': VBA 매크로를 실행하는 데 사용합니다. (pip install pywin32)

    다음은 이 라이브러리들을 이용해 이메일을 보내는 파이썬 코드입니다:


    import base64
    import smtplib
    import time
    from email.mime.image import MIMEImage
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    
    import pandas as pd
    import win32com.client
    from openpyxl.utils import column_index_from_string
    from openpyxl.utils.cell import coordinate_from_string
    
    받는_사람 = "google@gmail.com"
    보내는_사람 = "google@gmail.com"
    비밀번호 = ""
    
    
    def convert_cell_range(cell_range):
        """A1:B1과 같은 range 바꾸기"""
        top_left, bottom_right = cell_range.split(":")
    
        top_col, top_row = coordinate_from_string(top_left)
        bottom_col, bottom_row = coordinate_from_string(bottom_right)
    
        top_row_index = int(top_row) - 1
        top_col_index = column_index_from_string(top_col) - 1
        bottom_row_index = int(bottom_row)
        bottom_col_index = column_index_from_string(bottom_col)
    
        return slice(top_row_index, bottom_row_index), slice(
            top_col_index, bottom_col_index
        )
    
    
    def send_email():
        try:
            # 엑셀 파일 읽기
            df = pd.read_excel("내엑셀.xlsm", sheet_name="weekSum", header=None)
    
        except FileNotFoundError:
            print("Excel 파일을 찾을 수 없습니다.")
            return
    
        # 셀 범위를 df.iloc에 사용할 수 있는 형식으로 변환
        cell_range = convert_cell_range("A1:I27")  # weekSum 시트의 전체 데이터
        data = df.iloc[cell_range]
    
        # NaN 값을 "빈 공간"으로 대체
        data = data.fillna("")
    
        # "Unnamed" 컬럼 제거
        # data = data.loc[:, ~data.columns.str.contains("^Unnamed")]
    
        # 이메일 메시지 생성
        msg = MIMEMultipart()
        msg["From"] = 보내는_사람  # 발신자 이메일
        msg["To"] = 받는_사람  # 수신자 이메일
        msg["Subject"] = "주간 데이터 첨부"  # 메일 제목
    
        # 이미지 읽기
        with open("chart.png", "rb") as file:
            img_data = file.read()
            img_b64 = base64.b64encode(img_data).decode()
    
        # 이미지 첨부
        img = MIMEImage(img_data, "png")
        img.add_header("Content-ID", "<chart>")  # angle bracket
        img.add_header("Content-Disposition", "inline", filename="chart.png")
        msg.attach(img)
    
        # 데이터를 HTML 테이블로 변환하고 이메일에 첨부
        html = """
    <html>
    <head>
    <style>
    table, th, td {{
    border: 1px solid black;
    border-collapse: collapse;
    padding: 5px;
    }}
    </style>
    </head>
    <body>
    {0}
    <br>
    <hr>
    <br>
    <img src="cid:chart" alt="Chart" style="">
    </body>
    </html>
    """.format(
            data.to_html(na_rep="")
        )
    
        msg.attach(MIMEText(html, "html"))
    
        # 데이터를 이메일에 첨부
        # msg.attach(MIMEText(data.to_string(), "plain"))
    
        try:
            # 이메일 서버에 로그인
            server = smtplib.SMTP("smtp.gmail.com", 587)
            server.starttls()
            server.login(msg["From"], 비밀번호)  # 비밀번호
    
            # 이메일 보내기
            server.sendmail(msg["From"], msg["To"], msg.as_string())
            server.quit()
            print("이메일을 성공적으로 보냈습니다.")
        except smtplib.SMTPException:
            print("오류: 이메일을 보낼 수 없습니다.")
    
    
    def save_chart():
        # Excel app
        xl = win32com.client.Dispatch("Excel.Application")
    
        # 딜레이
        time.sleep(1)
        xl.Visible = True
    
        # 짜놓은 VBA 매크로 실행
        wb = xl.Workbooks.Open(r"D:\내엑셀.xlsm")  # 절대 경로
    
        # 매크로 호출 (Module1 이름은 기본값)
        xl.Application.Run("Module1.SaveChartAsPNG")
    
        wb.Save()
        # xl.Application.Quit()  # 알아서 엑셀 닫기
    
    
    if __name__ == "__main__":
        save_chart()  # xlsm에서 이미지 먼저 추출/저장
        send_email()  # 이메일 보내기 함수 호출



    이 코드는 크게 두 부분으로 구성되어 있습니다. 먼저 'save_chart()' 함수를 통해 VBA 매크로를 호출하여 엑셀 차트를 PNG 이미지로 저장하고, 그다음 'send_email()' 함수를 통해 이메일을 보내는 작업을 수행합니다.

     

    마치며

    이렇게 파이썬과 VBA를 이용하면 엑셀 데이터와 차트를 이메일로 손쉽게 보낼 수 있습니다.

    728x90

    댓글