imap指定邮箱搜索特殊邮件下载——合理利用search
作者:故笺笺
链接地址:https://www.cnblogs.com/gujianjian/p/12762040.html
说明:码农不易,请尊重他人劳动成果共创和谐网络环境。本文转载请备注原作出处,违者必究。
背景:上一篇我们描述了怎么下载指定邮箱的所有邮件的附件和正文,没有附件就下载正文,主要是考虑到如果发件人并没有按照附件的格式下载,然后发文本放在了正文里面灯因素,也主要是为了统计交了多少个作业,未交的有多少,以方便提醒对方提交......而这次的需求是,每天会有固定的人发送固定的邮件给我,我需要每天下载,并进行数据处理,发送给各个老板。简单而言就是下载邮箱(imap)->数据处理(pandas)->文件发送微信(请查看我历史博客有讲解怎么发送文件到微信)定期轰炸老板!!!!!
一、登录邮箱,我是封装成一个小def,方便调用。
def Start_mailbox(host,post,uesr,passwrod):#启动imap邮箱服务(作者:故笺笺) try: conn = imaplib.IMAP4(host, post) conn.login(uesr, passwrod) print("Connect to {0}:{1} successfully".format(host, post)) return conn except BaseException as e: print("Connect to {0}:{1} failed".format(host, post), e)
主函数主要是设置各种参数,和调用def,其他的没了。
# ******************主程序(作者:故笺笺)************************# #参数设置 pattern_uid = re.compile('\d+ \(UID (?P<uid>\d+)\)') #设置获取UID的正则表达式,通过UID去进行文件的copy和delect Download_path = r'E:\邮箱下载' #设置邮箱下载路径 old_maildir = 'INBOX' #收件箱 new_maildir = '&XfJfUmhj-/&Ytt1H4v0Zg5PGmVwY24-' #移动的目标邮箱 rubbish_maildir = '&XfJfUmhj-/&U4ZT8leDVz5lh072-' #垃圾箱 host = 'imap.263.net' post = '143' uesr = '***********.com' passwrod = '*******' #启动imap邮箱服务 conn = Start_mailbox(host,post,uesr,passwrod) imap4(conn,old_maildir,new_maildir,Download_path) ''' #查看imap邮箱有多少个文件夹,imap邮箱名字不是ut-f8编码,不能自动编译,需自行查看 try: type_, folders = conn.list() for i in folders: print(i.decode(type_)) except BaseException as e: print("the {0}:{1} no file".format("imap.263.net", 143), e) '''
二、imap4(conn,old_maildir,new_maildir,Download_path) def 的使用
这里我们讲一下search的用法,第一个参数是指编码方式,第二个方式类似于筛选,ALL表示整个文件夹下的所有文件,或者指定某种规律的方式来进行筛选邮件,具体的参数设定可以参考相应的文献内容。此处我们就得到了收件箱里面发送人是"**aaaa**.com"的所有邮件了,然后把找到的邮件列表传递给list。
conn.select(old_maildir, readonly=True) #打开指定的邮箱文件下,我的old_maildir指定的是收件箱,一般收到的文件也都会在此处。 type_, data = conn.search('GB2312','FROM "**aaaa**.com"') #重点中的重点 mail_list = data[0].split()#传输当前文件夹下的文件给mail_list
接下来,我们根据每个邮箱的id,就可以来操作了。当然,这里我只是操作了下载文件,类似于正文,音频,图片等请参考其他博客文章。
mail_list = data[0].split()#传输当前文件夹下的文件给mail_list,作者:故笺笺 print('{}个文件被找到!'.format(len(mail_list))) for num in mail_list: type_, data = conn.fetch(num,'(RFC822)') msg = email.message_from_string(data[0][1].decode('utf-8'))#传输邮件全部内容,用email解析 From_mail = email.utils.parseaddr(msg.get('from'))[1] From_mail_name = From_mail.split('@')[0] mail_title,mail_charset = email.header.decode_header(msg.get('Subject'))[0] mail_title = mail_title.decode(mail_charset) print(mail_title) for part in msg.walk(): if not part.is_multipart(): filename = part.get_filename() #如果是附件,这里就会取出附件的文件名 if filename: print('下载文件') fname,file_charset = email.header.decode_header(filename)[0] fname = fname.decode(file_charset) attach_data = part.get_payload(decode=True) savefile(fname, attach_data, Download_path) else: print('不是附件') pass #下载一个文件之后把这个文件移动到新的邮件文件夹,以便后面遍历for少一些数据。内容在下面。print ('</br>') pcount += 1
由于我收到的文件都是中文,这里就会涉及到邮件标题的解码和附件,一般解码我们用到的是email的email.header.decode_header,这样解码下来的是一个list,第一个参数是字符,第二个参数是编码方式,然后我们再把字符按照它给的编码方式进行编码,就可以得到我们的中文。
fname,file_charset = email.header.decode_header(filename)[0]
fname = fname.decode(file_charset)
得到附件的中文名称,我们还要获取附件,并且保存,保存用open的方法,也可以参考with会更好,,当然对于这种小功能,最好封装成def,便于代码的阅读和维护。
def savefile(filename, data, path):#保存文件方法(保存在path目录下) try: filepath = path +r'\\'+ filename print(filepath) fn = open(filepath, 'wb') except: print('filename error') fn.close() fn.write(data) fn.close()
得到附件,并调用保存的def:
attach_data = part.get_payload(decode=True)
savefile(fname, attach_data, Download_path)
其实想一想,如果我们就这样完了,每一次,我们都会去查找收件箱里面的发送人是"**aaaa**.com"的所有邮件然后下载,第二次我们是不是会重复下载之前的数据,如果长期下来,你邮箱已经有1000份邮件了,那就要下载1000次,这样的代码执行起来是非常不perfect的,所以,基于imap的邮箱操作,我实现的方法是每一次操作,都把邮件移动到新的文件夹,并删除收件箱里的文件,这样就可以保证,每次我从收件箱收到的都是发送人最新发送的邮件。
#下载一个文件之后把这个文件移动到新的邮件文件夹,以便后面遍历for少一些数据。作者:故笺笺 try: resp,data = conn.fetch(num,'(UID)') match = pattern_uid.match(data[0].decode('utf-8')) msg_uid = match.group('uid') remove_email_file(old_maildir,new_maildir,msg_uid) except BaseException as e: print("remove email failed", e)
每个邮箱都有自己的uid,我们用pattern_uid = re.compile('\d+ \(UID (?P<uid>\d+)\)')这样的正则,去取出fetch里的uid,通过UID去进行COPY和delect,当然,按照我的臭习惯,我把复制和删除封装成了一个小def。
def remove_email_file(old_maildir,new_maildir,msg_uid):#移动邮箱文件夹 ,作者:故笺 try: conn.select(old_maildir, readonly=False) type_, data = conn.search(None,"ALL") result = conn.uid('COPY',msg_uid,new_maildir) print('copy successful:') if result[0]=='OK': conn.select(old_maildir, readonly=False) type_,data = conn.search(None,"ALL") mov,data = conn.uid('STORE',msg_uid, '+FLAGS', '(\\Deleted)') conn.expunge() print('delect successful:') except BaseException as e: print('fail error:',e)
这样就完成了每次下载指定发送人发来的邮件,并且每次都只执行一个for,只下载一次,大大优化了代码。
以下是全部的代码实现,分享给大家,不用每次都要去邮箱操作了,或许,你可以再深思一步,如何对收件箱里面的邮件进行分类,整理?
import imaplib import email from email.parser import Parser import re,os def Start_mailbox(host,post,uesr,passwrod):#启动imap邮箱服务 try: conn = imaplib.IMAP4(host, post) conn.login(uesr, passwrod) print("[+] Connect to {0}:{1} successfully".format(host, post)) return conn except BaseException as e: print("Connect to {0}:{1} failed".format(host, post), e) def savefile(filename, data, path):#保存文件方法(保存在path目录下) try: filepath = path +r'\\'+ filename print(filepath) fn = open(filepath, 'wb') except: print('filename error') fn.close() fn.write(data) fn.close() def remove_email_file(old_maildir,new_maildir,msg_uid):#移动邮箱文件夹 try: conn.select(old_maildir, readonly=False) type_, data = conn.search(None,"ALL") result = conn.uid('COPY',msg_uid,new_maildir) print('copy successful:') if result[0]=='OK': conn.select(old_maildir, readonly=False) type_,data = conn.search(None,"ALL") mov,data = conn.uid('STORE',msg_uid, '+FLAGS', '(\\Deleted)') conn.expunge() print('delect successful:') except BaseException as e: print('fail error:',e) def imap4(conn,old_maildir,new_maildir,Download_path): conn.select(old_maildir, readonly=True) type_, data = conn.search('GB2312','FROM "cuiyongle-fesco@sunlands.com"')#'(SUBJECT "招生说明会到课数据"'.encode('gb2312')指定标题 pcount = 1 mail_list = data[0].split()#传输当前文件夹下的文件给mail_list print('{}个文件被找到!'.format(len(mail_list))) for num in mail_list: type_, data = conn.fetch(num,'(RFC822)') msg = email.message_from_string(data[0][1].decode('utf-8'))#传输邮件全部内容,用email解析 From_mail = email.utils.parseaddr(msg.get('from'))[1] From_mail_name = From_mail.split('@')[0] mail_title,mail_charset = email.header.decode_header(msg.get('Subject'))[0] mail_title = mail_title.decode(mail_charset) print(mail_title) for part in msg.walk(): if not part.is_multipart(): filename = part.get_filename() #如果是附件,这里就会取出附件的文件名 if filename: print('下载文件') fname,file_charset = email.header.decode_header(filename)[0] fname = fname.decode(file_charset) attach_data = part.get_payload(decode=True) savefile(fname, attach_data, Download_path) else: print('不是附件') pass #下载一个文件之后把这个文件移动到新的邮件文件夹,以便后面遍历for少一些数据。 try: resp,data = conn.fetch(num,'(UID)') match = pattern_uid.match(data[0].decode('utf-8')) msg_uid = match.group('uid') remove_email_file(old_maildir,new_maildir,msg_uid) except BaseException as e: print("remove email failed", e) print ('</br>') pcount += 1 conn.close() conn.logout() # ******************主程序,作者,故笺,转载请备注出处,尊重别人劳动成果************************# #参数设置 pattern_uid = re.compile('\d+ \(UID (?P<uid>\d+)\)') Download_path = r'E:\邮箱下载' old_maildir = 'INBOX' new_maildir = '&XfJfUmhj-/&Ytt1H4v0Zg5PGmVwY24-' rubbish_maildir = '&XfJfUmhj-/&U4ZT8leDVz5lh072-' host = 'imap.263.net' post = '143' uesr = '*********.com' passwrod = '*********' #启动imap邮箱服务 conn = Start_mailbox(host,post,uesr,passwrod) imap4(conn,old_maildir,new_maildir,Download_path) ''' #查看imap邮箱有多少个文件夹,imap邮箱名字不是ut-f8编码,不能自动编译,需自行查看 try: type_, folders = conn.list() for i in folders: print(i.decode(type_)) except BaseException as e: print("the {0}:{1} no file".format("imap.263.net", 143), e) '''
在学习python的路上,懵懵懂懂,每天要查上百分博客,文献。走千万个坑.....其实我想认识想一起在python路上成长的小伙伴,一起努力学习成长,如果有意愿,志同道合一起学习,欢迎加入QQ群:878749917