背景:需要讀取郵件,對提交的申請進行處理,然后回復郵件。非常繁瑣,想要利用python實現自動處理。
第一步:通過IMAP協議讀取未讀郵件ID;解析標題和郵箱地址。
第二步:數據處理,標題提取出賬號;不符合規范的手動處理;提取標題符合規范的郵件對應郵箱地址。
第三步:通過SMTP協議批量發送郵件。
python3 imaplib庫的官方文檔
https://docs.python.org/3/library/imaplib.html?highlight=imaplib#module-imaplib
python3 email庫的官方文檔
https://docs.python.org/3/library/email.html?highlight=email#module-email
一、通過IMAP協議讀取文件,並提取標題和發件箱信息。
IMAP(Internet Mail Access Protocol,Internet消息訪問協議)是斯坦福大學在1986年開發的一種郵件獲取協議。IMAP協議運行在TCP/IP協議之上,使用的端口是143。常用的版本是IMAP4。
- 不用把所有的郵件全部下載,可以通過客戶端直接對服務器上的郵件進行操作。
- 有選擇的從郵件服務器接收郵件的功能、基於服務器的信息處理功能和共享信箱功能。
- 通過瀏覽信件頭來決定是否收取、刪除和檢索郵件的特定部分
- 在服務器上創建或更改文件夾或郵箱。
- 支持脫機操作模式,不同於POP3,它不會自動刪除在郵件服務器上已取出的郵件
- 還支持聯機操作和斷連接操作,將郵件服務器作為“遠程文件服務器”進行訪問,更加靈活方便。
- IMAP4支持多個郵箱。
1 # -*- coding:utf-8 -*- 2 # 登錄郵箱並發送郵件。 3 import imaplib 4 import email 5 from email.header import decode_header 6 7 '''第一部分:收件IMAP4********************************************''' 8 '''登錄郵箱IMAP4==========================================================''' 9 from_addr = 'a@b.com' # 發件郵箱 10 password = 'password' # 郵箱密碼(或者客戶端授權碼) 11 imap_server = 'imap.qiye.163.com' 12 13 try: 14 email_server = imaplib.IMAP4_SSL(imap_server, 993) #網易企業郵箱服務器及SSL端口 15 print("imap4 服務器連接成功") 16 except: 17 print("imap4 服務器連接失敗") 18 exit(1) 19 20 try: 21 email_server.login(from_addr, password) 22 print("imap4 賬號密碼正確,登錄成功") 23 24 except: 25 print("imap4 賬號密碼不正確,登錄失敗") 26 exit(1)
2.獲取未讀郵件ID,解析郵件標題和郵箱地址。
1 ''' 郵箱中收到的未讀郵件的數量==========================================================''' 2 email_server.select() 3 #未讀郵件封數 email_unseen_count 4 email_unseen_count = len(email_server.search(None, 'UNSEEN')[1][0].split()) #所有未讀郵件 5 print('未讀郵件一共有:',email_unseen_count,'封') 6 #得到所有未讀郵件標號ID的byte格式 email_unseen_id_byte [b'5255', b'5256', b'5257'] 7 email_unseen_id_byte = email_server.search(None, 'UNSEEN')[1][0].split() #[b'5255', b'5256', b'5257'] 8 9 #得到所有未讀郵件標號將byte格式轉為為str email_unseen_id ['5255', '5256', '5257', '5258'] 10 email_unseen_id = [] 11 count_byte = 0 12 for row in email_unseen_id_byte: 13 email_unseen_id.append(row.decode('utf-8')) 14 print(email_unseen_id) 15 16 '''讀取郵件標題,地址=========================================================''' 17 # 通過fetch(index)讀取第index封郵件的內容 18 sub_list = [] 19 addr_list = [] 20 21 #對每一封郵件進行處理 22 for a in email_unseen_id: 23 # 獲取郵件主題和地址信息,byte格式 24 typ, email_content = email_server.fetch(f'{a}'.encode(), '(RFC822)') 25 mail_text = email_content[0][1] 26 msg = email.message_from_bytes(mail_text) 27 subject = msg['Subject'] 28 email_from = msg['from'] 29 subdecode = decode_header(subject) #[(b'\xc9\xed\xdd\xc8\xcf\xd6\xbb\xd8\xcc\xc2\xeb', 'gb18030')] 30 from_decode = decode_header(email_from) # [(b'"', None), (b'\xd0\xa1\xe3', 'gb18030'), (b'" <3825@qq.com>', None)] 31 print(from_decode) 32 33 #將郵件信息由byte格式轉換為str 34 if subdecode[0][1] == None: #[(b'"', None), (b'\xd8\xafA1\xc9\xb5\xb9\xcf', 'gb18030'), (b'" <970@qq.com>', None)] 35 print(subdecode[0][1]) 36 37 else: 38 addr_list.append(addr_list.append(from_decode[2][0].decode('utf-8'))) 40 sub_list.append(subdecode[0][0].decode(subdecode[0][1])) #<class 'str'> 41 42 print('sub_list:', sub_list) #['找回', '楊 1702'] 43 print('addr_list:', addr_list) # ['" <385@qq.com>', '" <51@qq.com>']
3.關閉IMAP連接
1 '''關閉IMAP4 select=========================================================''' 2 # 關閉select 3 email_server.close() 4 # 關閉連接 5 email_server.logout()
二、數據處理:提取能處理的賬號和郵箱地址
1.提取標題中含有8位或10位的數字,其余用X標記
1 import re # 使用正則表達式提取數字。 https://blog.csdn.net/qq_38486203/article/details/80309478 2 import pandas as pd 3 5 '''提取賬號=============================================''' 6 sub_op = [] 7 addr_op = [] 8 9 sub_op_f = [] 10 addr_op_f = [] 11 12 count = 0 13 count_f = 0 14 15 for a in sub_list: 16 if re.findall(r'\d+',a) == []: 17 sub_op_str = 'x' 18 else: 19 sub_op_str_num = str(re.findall(r'\d+',a))[2:-2] 20 #選出8位或10位賬號 21 if len(sub_op_str_num) == 8 or len(sub_op_str_num) == 10: 22 sub_op_str = sub_op_str_num 23 else: 24 sub_op_str = 'x' 25 sub_op.append(sub_op_str) 26 27 print("郵件主題中的賬號為:",sub_op,';') #['x', '1700000000', 'x']
2.提取有效郵件對應的郵箱
'''提取郵箱地址=============================================''' for b in addr_list: addr_op_str = b.strip()[3:-1] #去掉頭尾 ['" <38@qq.com>', '" <31@qq.com>'] addr_op.append(addr_op_str) #print(addr_op) #['38@qq.com', '31@qq.com', 'a1@qq.com'] '''刪除主題用X標記的賬號和地址=============================================''' for c in sub_op: if c == 'x': count = count+1 else: sub_op_f.append(sub_op[count]) addr_op_f.append(addr_op[count]) count = count+1 print("郵件中地址為:",addr_op_f) # <class 'list'>
3.利用pandas讀取並保存為csv備查
1 '''把這一批次所有讀取的賬號地址和處理的賬號地址都導出到EXCLE存檔備查=============================================''' 2 import pandas as pd 3 # 讀取所有未讀主題,賬號,地址並保存 4 data_unseen = [sub_list,sub_op,addr_op] 5 data_unseen1 = pd.DataFrame(data_unseen) 6 data_unseen1.to_csv('data_unseen.csv',sep=',', header=True, index=True,encoding='utf_8_sig') 7 8 # 讀取所有已處理郵件的主題,賬號,地址並保存 9 data_op = [sub_list,sub_op_f,addr_op_f] 10 data_op1 = pd.DataFrame(data_op) 11 data_op1.to_csv('data_op.csv',sep=',', header=True, index=True,encoding='utf_8_sig')
三、發送郵件
1.連接SMTP服務器並登錄
1 # 登錄郵箱並發送郵件。 2 from email.mime.text import MIMEText 3 from email.header import Header 4 import smtplib 5 6 #登錄郵箱 7 smtp_server = 'smtp.qiye.163.com' # 企業郵箱地址,若是個人郵箱地址為:smtp.163.com 8 server = smtplib.SMTP_SSL(smtp_server, 994) # 第二個參數為默認端口為25,這里使用ssl,端口為994 9 from_addr = 'a@b.com' # 發件郵箱 10 password = 'password' # 郵箱密碼(或者客戶端授權碼) 11 12 try: 13 print('開始登錄') 14 server.login(from_addr, password) # 登錄郵箱 15 print('登錄成功') 16 except Exception as e: 17 print('Error:', e)
2.發送郵件並退出
1 print("郵件開始發送") 2 3 message = '''您好,這是一封測試郵件''' 6 msg = MIMEText(message, 'plain', 'utf-8') 7 8 msg['Subject'] = Header("回復:測試", 'utf-8') 9 msg['From'] = Header('a@b.com') 10 11 to_addr_list = ['a@qq.com'] 13 14 try: 15 for to_addr in to_addr_list: 16 msg['To'] = Header(to_addr, 'utf-8') 17 server.sendmail(from_addr, to_addr, msg.as_string()) # 將msg轉化成string發出 18 print("郵件發送成功") 23 24 except Exception as e: 25 print('Error:', e) 26 27 server.quit()
四、小結
1.在功能上實現了批量處理未讀郵件,但是需要分別執行對應的代碼,比較啰嗦,計划后續利用圖形界面的方式進行點擊處理。
2.當處理的數據過多時,發現部分163或qq郵箱的郵件標題只有兩個參數,執行 from_decode[2][0].decode('utf-8') 會報錯超出list的定義范圍。所以需要多一個判斷 len,不同的長度,取的值不一樣。
3.越往下做,要解決的業務上的細節問題越多。