python——imap指定邮箱搜索特殊邮件下载

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

志同道合一起学习,欢迎加入QQ群:878749917
原文地址:https://www.cnblogs.com/gujianjian/p/12762040.html