版本
python 3.8
imapclient:3.0.0?
注意: 這里是以騰訊企業(yè)郵箱為例,不同的郵箱可能會(huì)有不同的編碼格式,就可能會(huì)產(chǎn)生錯(cuò)誤。
郵件協(xié)議概述
-
SMTP (Simple Mail Transfer Protocol):
- SMTP 是用于發(fā)送電子郵件的標(biāo)準(zhǔn)協(xié)議。
- 客戶端使用 SMTP 將郵件發(fā)送到郵件服務(wù)器。
- SMTP 客戶端連接到 SMTP 服務(wù)器的 25 號(hào)端口。
- SMTP 協(xié)議通常用于將郵件從發(fā)送方傳輸?shù)浇邮辗降泥]件服務(wù)器。
-
POP3 (Post Office Protocol version 3):
- POP3 是用于從郵件服務(wù)器上獲取郵件的協(xié)議。
- 客戶端使用 POP3 從郵件服務(wù)器下載郵件到本地計(jì)算機(jī)。
- POP3 客戶端連接到 POP3 服務(wù)器的 110 號(hào)端口。
- POP3 協(xié)議通常會(huì)將郵件從服務(wù)器上下載到客戶端,并在下載后從服務(wù)器上刪除郵件。
-
IMAP (Internet Message Access Protocol):
- IMAP 也是用于從郵件服務(wù)器上獲取郵件的協(xié)議,與 POP3 類似,但提供了更多的功能。
- IMAP 允許客戶端在多個(gè)設(shè)備上同步查看郵件,因?yàn)猷]件仍然保留在服務(wù)器上。
- IMAP 客戶端連接到 IMAP 服務(wù)器的 143 號(hào)端口。
- IMAP 協(xié)議通常更適合需要在多個(gè)設(shè)備上訪問郵件的用戶,因?yàn)樗试S在所有設(shè)備上同步郵件狀態(tài)。
這里列舉了三種協(xié)議,其中SMTP用于發(fā)送郵件,POP3和IMAP用于獲取郵件,由于IMAP更強(qiáng)大,所有本文的郵件獲取也是基于IMAP協(xié)議。
從郵箱獲取郵件 (標(biāo)題、內(nèi)容、附件等)
python雖然自帶imaplib這個(gè)包,但用起來比較復(fù)雜,所以在這一部分,本文使用imapclient,這是一個(gè)用于 IMAP 客戶端的第三方庫(kù),提供了更高級(jí)的功能,例如搜索、標(biāo)記、文件夾管理等,它比標(biāo)準(zhǔn)庫(kù)中的?imaplib
?更易于使用。
1. 郵箱登錄
from imapclient import IMAPClient
# 郵箱登錄
email_address = 'xxxx@xxxx.com' # 郵箱地址
password = 'xxxxxx' # 密碼
# 郵箱服務(wù)器信息
s_mail = IMAPClient('imap.exmail.qq.com') # HOST = "imap.host.com"
s_mail.login(email_address, password)
2. 選擇郵箱 select_folder
就是選擇如下所示:
可以先查看郵箱文件夾
選擇郵箱文件夾
# 選擇郵箱
s_mail.select_folder(folder='inbox', readonly=True) # 這里folder也可以選擇 已發(fā)送等
運(yùn)行結(jié)果:
這里可以看到,選擇不同郵箱會(huì)收到不同的返回結(jié)果,返回的結(jié)果是字典格式的,一般不會(huì)用到,這里也沒有去賦值。
3. 郵件篩選 search
在選擇好郵箱之后,比如收件箱,會(huì)拿到大量的郵件,search我看代碼可以根據(jù)標(biāo)題、正文、時(shí)間、標(biāo)記(‘SEEN’, 'UNSEEN', 'Flagged')等,但是實(shí)際上無法通過標(biāo)題和正文檢索,我想可能原因是跟郵箱編碼格式有關(guān),這里我查了很多資料依舊沒有解決,但是可以通過遍歷郵件的方法實(shí)現(xiàn)。
通過時(shí)間區(qū)間篩選? 這里的時(shí)間要轉(zhuǎn)換為datetime的格式??
# 從 20240101 這一天開始
message_ids = s_mail.search(['SINCE', datetime.date(2024, 1, 1)])
# 在 20240101 這一天之前 不包括20240101
message_ids = s_mail.search(['BEFORE', datetime.date(2024, 1, 1)])
# 在某個(gè)時(shí)間區(qū)間
since_date = datetime.datetime.strptime(search_date_since, '%Y%m%d')
before_date = datetime.datetime.strptime(search_date_before, '%Y%m%d')
message_ids = s_mail.search(['SINCE', since_date, 'BEFORE', before_date])
通過多個(gè)條件篩選 比如在上面的基礎(chǔ)上加一個(gè) “未讀”
message_ids = s_mail.search(['SINCE', since_date, 'BEFORE', before_date, 'UNSEEN'])
4. 獲取郵件數(shù)據(jù) fetch
通過 fetch
方法,可以獲取郵件的各種信息,如郵件的正文、發(fā)件人、收件人、主題、日期等。
以下是 imapclient
中 fetch
方法的一般用法:
fetch_data = s_mail.fetch(message_ids, ['BODY[]', 'FLAGS', 'INTERNALDATE', 'RFC822.SIZE'])
這里的 message_ids
是一個(gè)郵件的序號(hào)或 UID 列表,用于指定要檢索的郵件。第二個(gè)參數(shù)是一個(gè)列表,包含了你想要檢索的郵件屬性。常見的郵件屬性包括:
-
'BODY[]'
: 獲取郵件的全部?jī)?nèi)容,包括頭部和正文。 -
'FLAGS'
: 獲取郵件的標(biāo)志,如已讀、已刪除等。 -
'INTERNALDATE'
: 獲取郵件的內(nèi)部日期,即郵件的收到時(shí)間。 -
'RFC822.SIZE'
: 獲取郵件的大小。
除了以上列出的屬性外,還可以指定其他屬性,如 'BODY[HEADER]'
(獲取郵件的頭部)、'ENVELOPE'
(獲取郵件的信封信息,如發(fā)件人、收件人、主題等)等。這里的細(xì)則可以查看這篇文章python imap fetch的坑
一般在使用的時(shí)候,可以根據(jù)需求選擇屬性,這樣獲取的數(shù)據(jù)量會(huì)小一點(diǎn),速度會(huì)更快。
這里以‘BODY[]’舉例,目的是介紹獲取郵件全部?jī)?nèi)容后,如何獲取郵件中各個(gè)屬性的信息。
通過fetch(message_ids,?['BODY[]']) 獲得是一個(gè)字典的格式,可以自行輸出查看一下。
數(shù)據(jù)解析方法:
4.1 獲取郵件頭部信息
content = s_mail.fetch(uid, ['BODY[]'])[uid][b'BODY[]']
email_content = email.message_from_bytes(content)
# 第一種 解析 郵件頭部信息
# 像 收件人、發(fā)件人、抄送、標(biāo)題等 這些都是同樣的方式 所以以其中一種舉例
# 定義解碼函數(shù) (這個(gè))
def __decode_str__(hs):
"""
編碼處理
:param hs:
:return:
"""
if isinstance(hs, bytes):
hs = hs.decode()
if hs:
# loger.info(" ==== {}".format(hs[0]))
if hs[0] == "=":
s, de = decode_header(hs)[0] # s is bytes de='gbk'
failout = base64.b64encode(s).decode()
s = failout if not de else s.decode(de) # str(s, 'gbk') = s.decode('gbk')
return s
else:
return hs
return ''
# 獲取標(biāo)題
subject = __decode_str__(email_content['Subject'])
'''
以下都可以通過上述方式獲得
郵件頭部信息:
Subject:郵件主題
From:發(fā)件人
To:收件人
Date:日期時(shí)間
Cc:抄送
Bcc:暗送
Message-ID:消息唯一標(biāo)識(shí)符
In-Reply-To:回復(fù)郵件的消息標(biāo)識(shí)符
References:參考消息標(biāo)識(shí)符列表
'''
4.2 獲取郵件正文
envelope = s_mail.fetch(uid, ['BODY[]'])[uid][b'BODY[]']
email_content = email.message_from_bytes(content)
# 提取郵件正文內(nèi)容
if email_content.is_multipart():
for part in email_content.walk():
content_type = part.get_content_type()
if content_type == 'text/plain': # 只提取純文本正文
body = part.get_payload(decode=True).decode('gbk')
print("郵件正文內(nèi)容:")
print(body)
break
else:
body = email_content.get_payload(decode=True).decode('gbk')
print("郵件正文內(nèi)容:")
print(body)
4.3 獲取郵件附件
for part in email_content.walk():
fileName = part.get_filename()
fileName = __decode_str__(fileName)
print(fileName)
# 附件下載
for part in email_content.walk():
fileName = part.get_filename()
fileName = __decode_str__(fileName)
if fileName:
with open(fileName, 'wb') as f:
data = part.get_payload(decode=True)
f.write(data)
郵件檢索 (遍歷)
上述三種方法沒有辦法直接獲取目標(biāo)郵件的指定信息的功能,由1select_flod,2search兩種方法,我們也只可以獲得一個(gè)范圍,在search函數(shù)中,可以看到subject字段,但是可能由于郵箱的問題,沒有達(dá)到通過標(biāo)題篩選的效果。所以郵件檢索的方法,可以理解為在一個(gè)范圍內(nèi)遍歷郵箱。
當(dāng)郵箱郵件數(shù)很多的時(shí)候,這個(gè)找到目標(biāo)郵件的方法會(huì)很慢,這里有一個(gè)小技巧,就是比如通過標(biāo)題找到目標(biāo)郵件時(shí),可以在fetch中選擇 'BODY[HEADER]',
這樣數(shù)據(jù)量相對(duì)較小,速度會(huì)稍微快一點(diǎn)。
以下是一個(gè)范例 (可以直接使用)
獲取一個(gè)時(shí)間區(qū)間內(nèi)的所有郵件,然后通過標(biāo)題篩選,再拿到目標(biāo)附件。文章來源:http://www.zghlxwxcb.cn/news/detail-855285.html
search_data_since、search_date_before: 字符串 like:'20240101'文章來源地址http://www.zghlxwxcb.cn/news/detail-855285.html
def get_excel_from_mail(search_date_since, search_date_before, target_subject, target_filename):
# 郵箱登錄
print("email login...")
email_address = 'xxx@xxx.com'
password = 'xxxxxxx'
# 郵箱服務(wù)器信息
# 搜索郵件
s_mail = IMAPClient('imap.exmail.qq.com')
s_mail.login(email_address, password)
s_mail.select_folder('收件箱', readonly=True)
since_date = datetime.datetime.strptime(search_date_since, '%Y%m%d').date()
# 指定日期范圍為since_date到since_date的后一天
next_day = datetime.datetime.strptime(search_date_before, '%Y%m%d').date()
result = s_mail.search(['SINCE', since_date, 'BEFORE', next_day])
print('SINCE', since_date, 'BEFORE', next_day)
for uid in reversed(result): # 優(yōu)先獲取最新的郵件
try:
subject = s_mail.fetch(uid, ['ENVELOPE'])[uid][b'ENVELOPE']
subject = __decode_str__(subject.subject)
print(subject)
if target_subject not in str(subject):
continue
except Exception as e:
print(e)
# print(s_mail.fetch(uid, ['ENVELOPE']))
continue
massageList = s_mail.fetch(uid, ['BODY[]'])
mailBody = massageList[uid][b'BODY[]']
# 郵件內(nèi)容解析最里面那層是按字節(jié)來解析郵件主題內(nèi)容,這個(gè)過程生成Message類型
try:
email_content = email.message_from_string(mailBody)
except TypeError:
email_content = email.message_from_bytes(mailBody)
for part in email_content.walk():
fileName = part.get_filename()
fileName = __decode_str__(fileName)
# print('fileName', fileName)
if fileName == target_filename:
print(' OK Subject = {}'.format(subject))
print('郵件附件下載')
# 附件下載
for part in email_content.walk():
fileName = part.get_filename()
fileName = __decode_str__(fileName)
if fileName == target_filename:
# savefile = os.path.join(dirs_email, fileName)
# with open(savefile, 'wb') as f:
# print('--------savefile')
data = part.get_payload(decode=True) # 獲取excel內(nèi)容 保存文件 -----
# f.write(data)
return data
return False
def __decode_str__(hs):
"""
編碼處理
:param hs:
:return:
"""
if isinstance(hs, bytes):
hs = hs.decode()
if hs:
# loger.info(" ==== {}".format(hs[0]))
if hs[0] == "=":
s, de = decode_header(hs)[0] # s is bytes de='gbk'
failout = base64.b64encode(s).decode()
s = failout if not de else s.decode(de) # str(s, 'gbk') = s.decode('gbk')
return s
else:
return hs
return ''
到了這里,關(guān)于python 郵箱自動(dòng)化操作(1) 郵件的自動(dòng)獲取 imapclient 非常詳細(xì)!有范例??!的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!