使用 IMAP & SMTP 來加強處理 Gmail
Table of Contents
#
Intro
Gmail 提供 IMAP & SMTP 的功能
###
內送郵件 (IMAP) 伺服器
- imap.gmail.com
- 需要安全資料傳輸層 (SSL):是
- 通訊埠:993
###
外寄郵件 (SMTP) 伺服器
- smtp.gmail.com
- 需要安全資料傳輸層 (SSL):是
- 需要傳輸層安全性 (TLS):是 (如果可用)
- 需要驗證:是
- 安全資料傳輸層 (SSL) 通訊埠:465
- 傳輸層安全性 (TLS)/STARTTLS 通訊埠:587
簡單的說 IMAP(Internet Message Access Protocol) 可以存取該 mail 的信件
SMTP(Simple Mail Transfer Protocol) 是可以利用該 mail 發信
詳細說明可以參照 wiki
另外還有 POP3(Post Office Protocol) 這個和 IMAP 類似的協定
主要 POP3 和 IMAP 的差異主要差在 POP3 是把信件內容拉下來, IMAP 不會, IMAP 是即時連線存取, 但是 IMAP 的好處就是即時連線同步, 這樣在多個裝置就可以即時的更新信件的狀態
所以在一些常見的客戶端程式例如 outlook 或是 Mac OS 裡面的郵件都是利用這些協定在處理 email
這邊當然也可以利用支援這些協定的程式語言寫一些功能來處理 email
#
使用情境
因為公司的服務每天會有大量的 feedback mail 寄到設定的 Gmail 信箱
雖然會有實習生協助整理和處理 feedback mail 但是還是很花人力
這年頭最貴的就是人力了
所以公司有個基於 Python 的程式在協助處理 feedback mail
那程式主要做的事情就是簡單的辨識 SPAM 與替 feedback 的內容打上 tag 以便於之後快速處理使用者的 feedback
#
How to do?
- 先開啟 Gmail 裡面的 IMAP 的功能
- 設定用於第三方應用程式驗證的密碼(並非 Google OAuth)
- 開始寫程式
#
怎麼開始寫 code?
這邊以 Python 為例
在開始前得先搞懂流程要幹什麼
- 連接 IMAP server
- 撈信件
- 讀取信件主旨(subject) 和信件內容(content) 和確認附件(attachement)
##
smaple code
以下使用 Python
###
建立 IMAP server 連去信箱讀取信件
from datetime import datetime, timedelta, date
import imaplib
import email
from email.header import decode_header
# pip install imap-tools
from imap_tools.imap_utf7 import encode, decode
ACCOUNT = '@gmail.com'
PWD = ''
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login(ACCOUNT, PWD)
status, data = mail.list()
mail.select('inbox')
# 取前一天的時間
today = (date.today() - timedelta(1)).strftime('%d-%b-%Y')
# 找出前一天未讀的信件
typ, data = mail.search(None, '(UNSEEN)', '(SENTSINCE {0})'.format(today))
# 取到 message id list(message id 就是每封信的 id)
ids = data[0]
id_list = ids.split()
for i in id_list:
typ, data = mail.fetch(i, '(RFC822)')
if typ == 'OK':
print('get mail ok')
for response_part in data:
if isinstance(response_part, tuple):
rawMail = response_part[1].decode()
# 取得信件
msg = email.message_from_string(rawMail)
print(msg)
基本上到這裡拿到 msg 就是這封信件的資訊了
就可以取出來做一些處理
例如: 判斷來信者來回信, 判斷內容替信件添加標籤 等等
關於 imap.search
可以如何使用可參考
Refer - Python’s imaplib (Example)
###
回信
幾本上回信也是寄信的一種
寄信的部分就是得用到 SMTP
幾本上的流程就是
-
連接 Gmail 的 SMTP server
-
寫信
-
寄信
-
關閉連接
以下為寄信範例
import email
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
smtp_server = smtplib.SMTP_SSL('smtp.gmail.com')
ACCOUNT = '@gmail.com'
PWD = ''
smtp_server.login(ACCOUNT, PWD)
send_mail = MIMEMultipart('mixed')
body = MIMEMultipart('alternative')
body.attach(MIMEText('test send mail', 'plain'))
body.attach(MIMEText('<div>test send mail<div>', 'html'))
send_mail.attach(body)
send_mail['message-id'] = email.utils.make_msgid()
send_mail['subject'] = ''
send_mail['to'] = ''
send_mail['from'] = '@gmail.com'
smtp_server.sendmail('@gmail.com', send_mail['to'], send_mail.as_string())
smtp_server.quit()
需要回信的話就得在發送時指定 references 和指定要回覆給哪封信(每一封信都有獨立的 message id)
所以會先用 imap 取到 mail list 這樣就會有 message id 的資訊了
以下為回信的範例
from datetime import datetime, timedelta, date
import imaplib
import email
from email.header import decode_header
# pip install imap-tools
from imap_tools.imap_utf7 import encode, decode
ACCOUNT = '@gmail.com'
PWD = ''
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login(ACCOUNT, PWD)
status, data = mail.list()
mail.select('inbox')
# 取前一天的時間
today = (date.today() - timedelta(1)).strftime('%d-%b-%Y')
# 找出前一天未讀的信件
typ, data = mail.search(None, '(UNSEEN)', '(SENTSINCE {0})'.format(today))
# 取到 message id list(message id 就是每封信的 id)
ids = data[0]
id_list = ids.split()
for i in id_list:
typ, data = mail.fetch(i, '(RFC822)')
if typ == 'OK':
print('get mail ok')
for response_part in data:
if isinstance(response_part, tuple):
raw_mail = response_part[1].decode()
# 取得信件
msg = email.message_from_string(raw_mail)
print(msg)
var_subject = msg['subject']
subject, encoding = decode_header(var_subject)[0]
# add origin content
for part in msg.walk():
if (part.get('Content-Disposition') and part.get('Content-Disposition').startswith("attachment")):
part.set_type('text/plain')
part.set_payload('Attachment removed: %s (%s, %d bytes)' % (part.get_filename(), part.get_content_type(), len(part.get_payload(decode=True))))
del part['Content-Disposition']
del part['Content-Transfer-Encoding']
reply_mail = MIMEMultipart('mixed')
body = MIMEMultipart('alternative')
body.attach(MIMEText('reply mail', 'plain'))
body.attach(MIMEText('<div>reply mail</div>', 'html'))
reply_mail.attach(body)
reply_mail['message-id'] = email.utils.make_msgid()
reply_mail['in-reply-to'] = msg['message-id']
reply_mail['references'] = msg['message-id']
reply_mail['subject'] = 're: ' + msg['subject']
reply_mail['to'] = msg['reply-to'] or msg['from']
reply_mail['from'] = '@gmail.com'
smtp_server.sendmail('@gmail.com', new['to'], new.as_string())