Python 全攻略 -- 第十五章 Python Emails 重點整理

Python 全攻略 - 第 15 章 Python Emails

前言

這篇筆記的內容及大綱,主要是按照 Wilson 老師分享的簡報檔中針對 Python Emails 第15章內容的安排順序及範圍所撰寫的。主要是介紹如何使用 Python 來發送 Email,以及如何使用 Python 來接收 Email 的相關技巧。

Sending Emails with Python
  • 傳統上,發送電子郵件很混亂,因為我們需要連接到 電子郵件伺服器,確認連接,設定協議,登入並發送 消息。例如, 簡單郵件傳輸協定 (SMTP) 是一種處理在郵件伺服器之間發送電子郵件和路由電子郵件的協定。

  • 幸運的是,Python 已為我們打造內建了一個 smtplib 的函式庫讓整個發送郵件的步驟變得簡單。 Python 所提供的 smtplib 模組,定義了 SMTP 用戶端會話對象,該對象可用於透過 SMTP 或 ESMTP(擴展 SMTP)監聽器,來將郵件傳送到任何 Internet 用戶電腦端。

  • 由於知名的公共 SMTP 寄件伺服器有很多,例如, google Gmail SMTP 伺服器的網域是:smtp.gmail.com,而 Microsoft Outlook 伺服器網域為: smtp.live.com,或是其他如 Hinet 的 SMTP 伺服器網域為:msa.hinet.net,等等。這裡老師則挑了 Gmail 來 Demo 如何使用 Python 使用 Gmail 來發送 Email。Gmail 也是大家日常中最常接觸及使用到的收發信服務。事實上,根據 2020 年第二季度的統計數據,全世界約有 10 億零 70 萬上線的活躍用戶。

Sending Emails with Python(2)

要在 Python 中發送電子郵件,首先,我們需要建立一個 smtp 的物件對象,而要創建 smtp 物件對象,你必須指定 stmp server 以及該 smtp server 的使用通訊埠號,以 Gmail 為例, Gmail 的 smtp server 為 smtp.gmail.com 而對應的通訊埠為 587 port;587 port 對應使用 RSA、Elliptic(橢圓曲線加密演算法) 以及搭配 Diffie-Hellman 金鑰交換演算法。

接著我們需要引入 smtplib 模組到程式中。由於 smtplib 是 python 內建模組,因此我們不需 install,可直接 import 來使用。

然後,我們可以以此來創建一個 Gmail 的 smtp 實例物件 → smtp_obj = smtplib.SMTP(“smtp.gmail.com”, 587),有了 smtp 實例物件後,我們就可以使用 ehlo() method 來向 SMTP 伺服器表明登入者您的身分。 使用 starttls() 方法來啟用 TLS 加密;使用 login(username, password) 來進行 SMTP 伺服器的登入;以及最最重要的,使用 sendmail(From_Email, To_Email, Mail_Content) 來寄發信件 。

由於 Gmail 不允許安全性較低的應用程式或裝置直接使用 User 的帳密來登入,取而代之的,我們必須向 Gmail 申請一組 for 程式使用的應用程式密碼(可以到這個官方連結來創建一組應用程式密碼),使用這個應用程式密碼透過程式來登入,而為了使用者的帳戶安全,Gmail 要求用戶端須先建立好帳戶的雙因子驗證,才能創建應用程式密碼。

簡言之,要使用 Python 透過 Gmail 發送信件,基本上就是先 import smtplib,然後創建 smtp class instance,最後再使用這個 instance 來登入以及寄送郵件。以下是一個範例,簡單展示了如何使用 smtplib 模組來發送 Gmail 郵件。

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# Gmail SMTP 伺服器設定
smtp_server = "smtp.gmail.com"
smtp_port = 587

# 寄件者和收件者的電子郵件地址
sender_email = "your_email@gmail.com"
receiver_email = "receiver_email@example.com"
password = "your_password"

# 建立 MIMEMultipart 物件
message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = "Python Email Test"

# 郵件內容
body = "這是一封使用 Python 發送的測試郵件。"
message.attach(MIMEText(body, "plain"))

try:
    # 連接到 Gmail SMTP 伺服器
    server = smtplib.SMTP(smtp_server, smtp_port)
    server.starttls()  # 啟用 TLS 加密
    server.login(sender_email, password)  # 登入 Gmail 帳戶
    server.sendmail(sender_email, receiver_email, message.as_string())  # 發送郵件
    print("郵件發送成功")
except Exception as e:
    print(f"郵件發送失敗: {e}")
finally:
    server.quit()  # 關閉連接
Receiving Emails with Python
  • 要使用 Python 來接收 Gmail 郵件,我們可以使用 Python 內建的 imaplib 模組。IMAP(Internet Message Access Protocol)是一種用於從遠程郵件伺服器檢索電子郵件的協議。IMAP 允許用戶在本地訪問郵件,而不需要下載郵件。IMAP 伺服器保留郵件在伺服器上的副本,而不是將其下載到用戶端。

  • 收信的步驟如下: 首先,我們需要引入 imaplib 模組到程式中,然後我們可以使用 imaplib.IMAP4_SSL(“imap.gmail.com”) 來建立一個 IMAP4_SSL 物件,並使用 login(username, password) 來登入 Gmail 帳戶。接著我們可以使用 list() 來列出信件虛擬資料夾集合,如: inbox 表收件夾,trash can 表垃圾桶,而 drafts 則表草稿資料夾;然後透過 select(mailbox) 來選擇郵件資料夾,例如 inbox 收件匣,然後使用 search(None, criterion) 來搜尋郵件,最後使用 fetch(id, “(RFC822)”) 來擷取郵件內容(郵件主旨、內容、收件日期等等)。

  • 以下是一個範例,簡單展示了如何使用 imaplib 模組來接收 Gmail 郵件。

import imaplib
import email
from email.header import decode_header

# Gmail IMAP 伺服器設定
imap_server = "imap.gmail.com"
email_user = "your_email@gmail.com"
email_pass = "your_password"

# 連接到 Gmail IMAP 伺服器
mail = imaplib.IMAP4_SSL(imap_server)

try:
    # 登入 Gmail 帳戶
    mail.login(email_user, email_pass)
    
    # 選擇收件匣
    mail.select("inbox")

    # 搜尋所有未讀郵件
    status, messages = mail.search(None, 'UNSEEN')

    # 取得郵件 ID 列表
    mail_ids = messages[0].split()

    for mail_id in mail_ids:
        # 取得郵件資料
        status, msg_data = mail.fetch(mail_id, '(RFC822)')
        
        for response_part in msg_data:
            if isinstance(response_part, tuple):
                msg = email.message_from_bytes(response_part[1])
                subject, encoding = decode_header(msg["Subject"])[0]
                if isinstance(subject, bytes):
                    subject = subject.decode(encoding if encoding else "utf-8")
                from_ = msg.get("From")
                print(f"Subject: {subject}")
                print(f"From: {from_}")

                # 如果郵件是多部分的
                if msg.is_multipart():
                    for part in msg.walk():
                        content_type = part.get_content_type()
                        content_disposition = str(part.get("Content-Disposition"))

                        try:
                            body = part.get_payload(decode=True).decode()
                        except:
                            pass

                        if content_type == "text/plain" and "attachment" not in content_disposition:
                            print(f"Body: {body}")
                else:
                    content_type = msg.get_content_type()
                    body = msg.get_payload(decode=True).decode()
                    if content_type == "text/plain":
                        print(f"Body: {body}")

except Exception as e:
    print(f"收取郵件失敗: {e}")
finally:
    mail.logout()  # 登出

【補充】 - 如何透過環境變數來存取應用程式密碼

透過 Gmail 官網申請產生的金鑰密碼,我們其實可以透過環境變數來存放在主程式以外的設定檔案中,這時就需要使用到 python-dotenv 這個套件來讀取檔案中設定 Gmail 登入帳密。

以下是一個簡單的範例,展示了如何使用 python-dotenv 來讀取 .env 設定檔案中的 Gmail 登入帳密。

在此之前,首先,您需要安裝 python-dotenv:

pip install python-dotenv

接著,建立一個 .env 檔案來儲存您的 Gmail 應用程式密碼:

# .env
GMAIL_USER=your_email@gmail.com
GMAIL_APP_PWD=your_app_password

然後,您可以使用以下範例程式碼來讀取這些環境變數並使用它們來登入 Gmail:

import os
from dotenv import load_dotenv
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# 載入 .env 檔案中的環境變數
load_dotenv()

# 從環境變數中取得 Gmail 帳戶和應用程式密碼
gmail_user = os.getenv("GMAIL_USER")
gmail_app_pwd = os.getenv("GMAIL_APP_PWD")

# Gmail SMTP 伺服器設定
smtp_server = "smtp.gmail.com"
smtp_port = 587

# 寄件者和收件者的電子郵件地址
sender_email = gmail_user
receiver_email = "receiver_email@example.com"

# 建立 MIMEMultipart 物件
message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = "Python Email Test"

# 郵件內容
body = "這是一封使用 Python 發送的測試郵件。"
message.attach(MIMEText(body, "plain"))

try:
    # 連接到 Gmail SMTP 伺服器
    server = smtplib.SMTP(smtp_server, smtp_port)
    server.starttls()  # 啟用 TLS 加密
    server.login(sender_email, gmail_app_pwd)  # 登入 Gmail 帳戶
    server.sendmail(sender_email, receiver_email, message.as_string())  # 發送郵件
    print("郵件發送成功")
except Exception as e:
    print(f"郵件發送失敗: {e}")
finally:
    server.quit()  # 關閉連接
3個讚