精华内容
下载资源
问答
  • 主要为大家详细介绍了python+POP3实现批量下载邮件附件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 主要为大家详细介绍了python实现批量解析邮件下载附件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • python批量下载邮件附件

    万次阅读 多人点赞 2018-08-20 18:16:29
    01.场景 老板让你调查个情况,你把excel...02.批量下载 python 提供的 email 包十分好用,功能完备,搞定我们这个需求,小菜一碟。 # 账户信息 email = 'xxx@chinastock.com.cn' password = 'xxx' pop3_...

     

    01.场景

    老板让你调查个情况,你把excel表发出去了,结果反馈回来的邮件有数百之多,如果一个一个的点开保存,肯定要加班了,让 python 来帮忙吧。

    02.批量下载

    python 提供的 email 包十分好用,功能完备,搞定我们这个需求,小菜一碟。

    # 账户信息
    email = 'xxx@chinastock.com.cn'
    password = 'xxx'
    pop3_server = 'mail.xxx.com.cn'
    # 连接到POP3服务器,带SSL的:
    server = poplib.POP3_SSL(pop3_server)
    # 可以打开或关闭调试信息:
    server.set_debuglevel(0)
    # POP3服务器的欢迎文字:
    print(server.getwelcome())
    # 身份认证:
    server.user(email)
    server.pass_(password)
    # stat()返回邮件数量和占用空间:
    msg_count, msg_size = server.stat()
    print('message count:', msg_count)
    print('message size:', msg_size, 'bytes')

    执行上面的代码,如果连接没有问题,那么应该能看到邮箱中邮件个数和邮件的总大小,单位是字节。

    这里先简化处理,不纠结于怎么过滤邮件了,主要考虑把附件下载到本地(收件箱里的全部邮件)。

    for i in range(1, msg_count):
        resp, byte_lines, octets = server.retr(i)
        # 转码
        str_lines = []
        for x in byte_lines:
            str_lines.append(x.decode())
        # 拼接邮件内容
        msg_content = '\n'.join(str_lines)
        # 把邮件内容解析为Message对象
        msg = Parser().parsestr(msg_content)
        headers = get_email_headers(msg)
        attachments = get_email_content(msg, r'E:\py\sendmail\attach')
        # 输出
        print('subject:', headers['Subject'])
        print('from:', headers['From'])
        print('to:', headers['To'])
        if 'cc' in headers:
            print('cc:', headers['Cc'])
        print('date:', headers['Date'])
        print('attachments: ', attachments)
        print('-----------------------------')

    retr() 的参数是个数字,可看成是编号,我们总共有 msg_count 封邮件,遍历这些邮件,解析后,保存附件到本地。

    byte_lines 是个 list,里面的元素是字节,因此,我们需要 decode() 转换一下。用换行符 \n 拼接形成的字符串,作为入参,构造成了Message对象。

    有 Message 对象,我们就可以通过解析这个对象,得到邮件的 header 和 content 了。

    先说 header。

    def get_email_headers(msg):
        headers = {}
        for header in ['From', 'To', 'Cc', 'Subject', 'Date']:
            value = msg.get(header, '')
            if value:
                if header == 'Date':
                    headers['Date'] = value
                if header == 'Subject':
                    subject = decode_str(value)
                    headers['Subject'] = subject
                if header == 'From':
                    hdr, addr = parseaddr(value)
                    name = decode_str(hdr)
                    from_addr = u'%s <%s>' % (name, addr)
                    headers['From'] = from_addr
                if header == 'To':
                    all_cc = value.split(',')
                    to = []
                    for x in all_cc:
                        hdr, addr = parseaddr(x)
                        name = decode_str(hdr)
                        to_addr = u'%s <%s>' % (name, addr)
                        to.append(to_addr)
                    headers['To'] = ','.join(to)
                if header == 'Cc':
                    all_cc = value.split(',')
                    cc = []
                    for x in all_cc:
                        hdr, addr = parseaddr(x)
                        name = decode_str(hdr)
                        cc_addr = u'%s <%s>' % (name, addr)
                        cc.append(to_addr)
                    headers['Cc'] = ','.join(cc)
        return headers

    header 是写键值对而已,我们关心的是 From, To, Cc, Subject, Date。Message 对象提供的接口,直接 get() 就好。对于收件人和抄送,可能是多人,要注意转换,decode_str() 函数是为了应对汉字乱码。

    下面的函数 get_email_content() 是用来下载附件的。

    def get_email_content(message, savepath):
        attachments = []
        for part in message.walk():
            filename = part.get_filename()
            if filename:
                filename = decode_str(filename)
                data = part.get_payload(decode=True)
                abs_filename = os.path.join(savepath, filename)
                attach = open(abs_filename, 'wb')
                attachments.append(filename)
                attach.write(data)
                attach.close()
        return attachments

    Message 里可能包含多个 MIMEBase,也就是多个 part,每个 part 里都可能有一个附件,message.walk() 遍历这些 part,依次解析。该函数把附件都保存到了 savepath 路径下了,不考虑附件重名的情况了。

    03.小结

    本文以较为简短的代码,展示了如何通过 python 批量下载邮件的附件。如果你有“邮件高度依赖症”,那么这种方法一定会给你提高数倍的工作效率。

    希望能帮到你!

    完整代码:

    # _*_ coding: utf-8 _*_
    
    import poplib
    import email
    import os
    from email.parser import Parser
    from email.header import decode_header
    from email.utils import parseaddr
    
    def decode_str(s):
        value, charset = decode_header(s)[0]
        if charset:
            if charset == 'gb2312':
                charset = 'gb18030'
            value = value.decode(charset)
        return value
    
    def get_email_headers(msg):
        headers = {}
        for header in ['From', 'To', 'Cc', 'Subject', 'Date']:
            value = msg.get(header, '')
            if value:
                if header == 'Date':
                    headers['Date'] = value
                if header == 'Subject':
                    subject = decode_str(value)
                    headers['Subject'] = subject
                if header == 'From':
                    hdr, addr = parseaddr(value)
                    name = decode_str(hdr)
                    from_addr = u'%s <%s>' % (name, addr)
                    headers['From'] = from_addr
                if header == 'To':
                    all_cc = value.split(',')
                    to = []
                    for x in all_cc:
                        hdr, addr = parseaddr(x)
                        name = decode_str(hdr)
                        to_addr = u'%s <%s>' % (name, addr)
                        to.append(to_addr)
                    headers['To'] = ','.join(to)
                if header == 'Cc':
                    all_cc = value.split(',')
                    cc = []
                    for x in all_cc:
                        hdr, addr = parseaddr(x)
                        name = decode_str(hdr)
                        cc_addr = u'%s <%s>' % (name, addr)
                        cc.append(to_addr)
                    headers['Cc'] = ','.join(cc)
        return headers
    
    def get_email_content(message, savepath):
        attachments = []
        for part in message.walk():
            filename = part.get_filename()
            if filename:
                filename = decode_str(filename)
                data = part.get_payload(decode=True)
                abs_filename = os.path.join(savepath, filename)
                attach = open(abs_filename, 'wb')
                attachments.append(filename)
                attach.write(data)
                attach.close()
        return attachments
    
    if __name__ == '__main__':
        # 账户信息
        email = 'xxx@xxx.com.cn'
        password = 'xxx'
        pop3_server = 'xxx.xxx.com.cn'
        # 连接到POP3服务器,带SSL的:
        server = poplib.POP3_SSL(pop3_server)
        # 可以打开或关闭调试信息:
        server.set_debuglevel(0)
        # POP3服务器的欢迎文字:
        print(server.getwelcome())
        # 身份认证:
        server.user(email)
        server.pass_(password)
        # stat()返回邮件数量和占用空间:
        msg_count, msg_size = server.stat()
        print('message count:', msg_count)
        print('message size:', msg_size, 'bytes')
        # b'+OK 237 174238271' list()响应的状态/邮件数量/邮件占用的空间大小
        resp, mails, octets = server.list()
    
        for i in range(1, msg_count):
            resp, byte_lines, octets = server.retr(i)
            # 转码
            str_lines = []
            for x in byte_lines:
                str_lines.append(x.decode())
            # 拼接邮件内容
            msg_content = '\n'.join(str_lines)
            # 把邮件内容解析为Message对象
            msg = Parser().parsestr(msg_content)
            headers = get_email_headers(msg)
            attachments = get_email_content(msg, r'E:\py\sendmail\attach')
    
            print('subject:', headers['Subject'])
            print('from:', headers['From'])
            print('to:', headers['To'])
            if 'cc' in headers:
                print('cc:', headers['Cc'])
            print('date:', headers['Date'])
            print('attachments: ', attachments)
            print('-----------------------------')
    
        server.quit()

    展开全文
  • python 批量下载邮件附件邮件登录信息改为自己的就行。 邮件服务器开通pop3,并生成授权码。 from email.parser import Parser from email.header import decode_header from email.utils import parseaddr ...

    python 批量下载邮件附件

    把邮件登录信息改为自己的就行。
    邮件服务器开通pop3,并生成授权码。

    from email.parser import Parser
    from email.header import decode_header
    from email.utils import parseaddr
    import poplib
    import os
    
    root = os.getcwd() 
    rootdir = root+"\\" 
    outdir = rootdir + "附件\\"
    def print_info(msg, indent=0):
        if indent == 0:
            for header in ['From', 'To', 'Subject']:
                value = msg.get(header, '')
                if value:
                    if header=='Subject':
                        value = decode_str(value)
                    else:
                        hdr, addr = parseaddr(value)
                        name = decode_str(hdr)
                        value = u'%s <%s>' % (name, addr)
                print('%s%s: %s' % ('  ' * indent, header, value))
        if (msg.is_multipart()):
            parts = msg.get_payload()
            for n, part in enumerate(parts):
                print('%spart %s' % ('  ' * indent, n))
                print('%s--------------------' % ('  ' * indent))
                print_info(part, indent + 1)
        else:
            content_type = msg.get_content_type()
            if content_type=='text/plain' or content_type=='text/html':
                content = msg.get_payload(decode=True)
                charset = guess_charset(msg)
                if charset:
                    content = content.decode(charset)
                print('%sText: %s' % ('  ' * indent, content + '...'))
            else:
                print('%sAttachment: %s' % ('  ' * indent, content_type))
    def decode_str(s):
        value, charset = decode_header(s)[0]
        if charset:
            value = value.decode(charset)
        return value
    
    def guess_charset(msg):
        charset = msg.get_charset()
        if charset is None:
            content_type = msg.get('Content-Type', '').lower()
            pos = content_type.find('charset=')
            if pos >= 0:
                charset = content_type[pos + 8:].strip()
        return charset
    email = "youemail"
    password = "you授权码"
    pop3_server = "pop.qq.com"
    # 连接到POP3服务器:
    # server = poplib.POP3(pop3_server)
    # qq需要使用ssl
    server = poplib.POP3_SSL(pop3_server)
    # 可以打开或关闭调试信息:
    server.set_debuglevel(0)
    # 可选:打印POP3服务器的欢迎文字:
    print(server.getwelcome().decode('utf-8'))
    # 身份认证:
    server.user(email)
    server.pass_(password)
    # stat()返回邮件数量和占用空间:
    print('Messages: %s. Size: %s' % server.stat())
    # list()返回所有邮件的编号:
    resp, mails, octets = server.list()
    # 可以查看返回的列表类似[b'1 82923', b'2 2184', ...]
    #print(mails)
    # 获取最新一封邮件, 注意索引号从1开始:
    index = len(mails)
    #self.print_info(msg)
    # 可以根据邮件索引号直接从服务器删除邮件:
    # server.dele(index)
    # 关闭连接:
    #print(msg)
    '''
    for i in range(index,0,-1):
        try:
            resp, lines, octets = server.retr(i)
            # lines存储了邮件的原始文本的每一行,
            # 可以获得整个邮件的原始文本:
            msg_content = b'\r\n'.join(lines).decode('utf-8')
            # 稍后解析出邮件:
            msg = Parser().parsestr(msg_content)
            value = msg.get('Subject', '')
            value = decode_str(value)
            
        except Exception as e:
            pass
        continue
    '''
    for i in range(index,0,-1):
        try:
            resp, lines, octets = server.retr(i)
            # lines存储了邮件的原始文本的每一行,
            # 可以获得整个邮件的原始文本:
            msg_content = b'\r\n'.join(lines).decode('utf-8')
            # 稍后解析出邮件:
            msg = Parser().parsestr(msg_content)
            for part in msg.walk():
                    file_name = part.get_filename()  # 获取附件名称类型
                    contType = part.get_content_type()
            filename = decode_str(str(file_name)) # 将附件名称可读化
            if not filename == 'None':
                data = part.get_payload(decode=True)  # 下载附件
                att_file = open(outdir +'('+str(i)+')'+ filename, 'wb')  # 在指定目录下创建文件,注意二进制文件需要用wb模式打开
                att_file.write(data)  # 保存附件
                value = msg.get('Subject', '')
                value = decode_str(value)
                print(value)
                print(filename+'保存完成!')
                att_file.close()
            
        except Exception as e:
            pass
        continue
    server.quit()
    
    展开全文
  • 批量下载QQ邮箱附件下载完后修改文件重命名 因为工作原因,需要处理QQ邮箱上来自各地网友的投稿附件。数量比较多(上千份),如果手动一个下载非常麻烦。。。 而且有些发来的附件命名也不规范,下载下来之后还需要...
  • 批量下载邮箱中的附件 可用、完善程序 比如使用邮箱收作业、收调查表之类。 加入了很多处理和筛选功能,还有解决一些编码问题,可用性肯定比网上几十行的最简单的脚本要好很多。曾经当助教时用Python写了个程序,...

    批量下载邮箱中的附件 可用、完善程序 支持IMAP和POP3

    比如使用邮箱收作业、收调查表之类。

    加入了很多处理和筛选功能,还有解决一些编码问题,可用性肯定比网上几十行的最简单的脚本要好很多。曾经当助教时用Python写了个程序,后来无聊就把它写得更完善了,很多细节都是查阅RFC文档的。

    很多邮件数据并没不规范,所以得做各种兼容处理。

    自己测了一个有2000封邮件的帐号基本不会有问题。

     

    肯定不止我需要,分享给大家,直接丢个Github地址。

    https://github.com/Li-Jiajie/BatchAttachmentDownloader

    可批量下载邮件中的附件,包含筛选功能,多种保存方式,比如按邮箱地址、按发件人之类的保存。

    支持POP3和IMAP两种协议。

     

    当然水平有限,要是不满足需求修改也是蛮方便的,里面注释和封装的都蛮全。

    把下面信息替换掉就可以跑了。

    有些邮箱(比如QQ邮箱)只允许访问最近一个月的邮件,在邮箱设置里可以调整,不是程序的问题。

    超大附件暂不支持,有需求的人可以在基础上改改代码。

    这里也放下代码,两个文件,一个main.py,一个email_helper.py。可移步Github获取最新版。

    main.py

    """
    https://github.com/Li-Jiajie/BatchAttachmentDownloader
    
    BatchAttachmentDownloader   v1.3.0
    邮件附件批量下载
    Python 3开发,支持IMAP4与POP3协议
    
    支持多种附件保存模式、筛选模式
    
    使用场景:通过邮箱收作业、调查等,批量下载附件    等
    
    2020.10.22
    Jiajie Li
    """
    
    import email_helper
    
    # ************************请设置以下参数************************
    
    # 邮箱地址  (必填)
    EMAIL_ADDRESS = '*****your email address*****'
    # 邮箱密码  (必填)
    EMAIL_PASSWORD = '*****your email password*****'
    
    # 邮件协议  (必填,POP3或IMAP)
    EMAIL_PROTOCOL = 'POP3'
    # 服务器地址(SSL)    (必填,请根据协议填入合适的地址)
    SERVER_ADDRESS = 'pop.qq.com'
    
    # 附件保存位置
    SAVE_PATH = 'F:\\Email-Attachments'
    # 筛选起止时间    yyyy-MM-dd HH:mm:ss
    DATE_BEGIN, DATE_END = '2020-10-20 00:00', '2020-11-5 18:00'  # 筛选起止时间(包含此时间)
    # 时区 默认东八区北京时间,如需更改请按如下格式更改
    TIME_ZONE = '+0800'
    # 筛选包含此内容的邮件地址,''表示全部邮件地址
    FROM_ADDRESS = ''
    # 筛选包含此内容的发件人昵称,''表示全部发件人昵称
    FROM_NAME = ''
    # 筛选包含此内容的邮件主题,''表示全部邮件主题
    SUBJECT = ''
    """
        保存模式    SAVE_MODE
    【0:所有附件存入一个文件夹】
    【1:每个邮箱地址一个文件夹】
    【2:每个邮件主题一个文件夹】
    【3:每个发件人的每个邮件主题一个文件夹】
    【4:每个发件人昵称一个文件夹】
    """
    SAVE_MODE = 1
    
    # ************************请设置以上参数************************
    
    
    if __name__ == '__main__':
        # 服务器连接与邮箱登录
        downloader = email_helper.BatchEmail(EMAIL_PROTOCOL, SERVER_ADDRESS, EMAIL_ADDRESS, EMAIL_PASSWORD)
    
        # 选项设置
        downloader.set_save_mode(SAVE_MODE)
        downloader.save_path = SAVE_PATH
        downloader.date_begin = DATE_BEGIN
        downloader.date_end = DATE_END
        downloader.time_zone = TIME_ZONE
        downloader.from_name = FROM_NAME
        downloader.from_address = FROM_ADDRESS
        downloader.subject = SUBJECT
    
        # 下载附件
        downloader.download_attachments()
        downloader.close()
    

    email_helper.py

    import abc
    import os
    import re
    from email.parser import Parser
    from email.header import decode_header
    from email.utils import parseaddr
    from email.message import Message
    import poplib
    import datetime
    import imaplib
    import email
    
    
    # 邮件信息类
    class EmailInfo(object):
        def __init__(self):
            self.date = None
            self.subject = None
            self.from_address = None
            self.from_name = None
            self.size = None
            self.attachments_name = []
    
        # 返回易阅读的文件大小字符串(两位小数),如 12345678 bytes返回'11.77MB'
        @staticmethod
        def bytes_to_readable(bytes_size: int):
            size_unit = [' Bytes', ' KB', ' MB', ' GB', ' TB', ' PB']
            unit_index = 0
            easy_read_size = bytes_size
            while easy_read_size >= 1024:
                easy_read_size /= 1024
                unit_index += 1
            if unit_index == 0:
                return str(easy_read_size) + size_unit[unit_index]
            else:
                return '{:.2f}'.format(easy_read_size) + size_unit[min(len(size_unit), unit_index)]
    
        def print_info(self):
            print('subject:', self.subject)
            print('from_address:', self.from_address)
            print('from_name:', self.from_name)
            print('date:', self.date)
            print('attachments:', self.attachments_name)
            print('total size:', self.bytes_to_readable(self.size))
            print('-----------------------------')
    
        def add_attachment_name(self, attachment_name):
            self.attachments_name.append(attachment_name)
    
    
    # 附件储存类_基类
    class Saver(metaclass=abc.ABCMeta):
        __SUBJECT_MAX_LENGTH = 51
    
        @abc.abstractmethod
        def __init__(self, root_path, file_name, file_data):
            self._root_path = root_path
            self._file_name = file_name
            self._file_data = file_data
    
        def _save_file(self, directory_path):
            # 储存文件,directory_path是绝对路径,不包含文件名
            if not os.path.exists(directory_path):
                os.makedirs(directory_path)
            self._file_name = Saver.file_name_check_and_update(directory_path, self._file_name)
            file = open(os.path.join(directory_path, self._file_name), 'wb')
            file.write(self._file_data)
            file.close()
    
        @staticmethod
        # 检查文件名,如果相同则自动递增编号。返回文件名,不包含路径。
        def file_name_check_and_update(path, file_name):
            file_number = 2
            exist_file_list = os.listdir(path)
            pure_name, extension = os.path.splitext(file_name)
            while file_name in exist_file_list:
                file_name = pure_name + '_' + str(file_number) + extension
                file_number += 1
            return file_name
    
        @staticmethod
        # 检查文件夹名词,去除非法字符并控制长度
        def normalize_directory_name(directory_name):
            normalized_name = re.sub('[*"/:?|<>\n]', '', directory_name, 0)
            normalized_name = normalized_name[0:min(Saver.__SUBJECT_MAX_LENGTH, len(normalized_name))].strip()
            return normalized_name
    
        @abc.abstractmethod
        def save(self):
            pass
    
    
    # 模式0:所有附件存入一个文件夹
    class MergeSaver(Saver):
        def __init__(self, root_path, file_name, file_data):
            super().__init__(root_path, file_name, file_data)
    
        def save(self):
            self._save_file(self._root_path)
    
    
    # 模式1:每个邮箱地址一个文件夹
    class AddressClassifySaver(Saver):
        def __init__(self, root_path, file_name, file_data, email_address):
            super().__init__(root_path, file_name, file_data)
            self._email_address = self.normalize_directory_name(email_address)
    
        def save(self):
            self._save_file(os.path.join(self._root_path, self._email_address))
    
    
    # 模式2:每个邮件主题一个文件夹
    class SubjectClassifySaver(Saver):
        def __init__(self, root_path, file_name, file_data, email_subject):
            super().__init__(root_path, file_name, file_data)
            self._email_subject = self.normalize_directory_name(email_subject)
    
        def save(self):
            self._save_file(os.path.join(self._root_path, self._email_subject))
    
    
    # 模式3:每个发件人的每个邮件主题一个文件夹
    class AddressSubjectClassifySaver(Saver):
        def __init__(self, root_path, file_name, file_data, email_address, email_subject):
            super().__init__(root_path, file_name, file_data)
            self._email_address = self.normalize_directory_name(email_address)
            self._email_subject = self.normalize_directory_name(email_subject)
    
        def save(self):
            self._save_file(os.path.join(self._root_path, self._email_address, self._email_subject))
    
    
    # 模式4:每个发件人昵称一个文件夹
    class AliasClassifySaver(Saver):
        def __init__(self, root_path, file_name, file_data, from_alias):
            super().__init__(root_path, file_name, file_data)
            self._from_alias = self.normalize_directory_name(from_alias)
    
        def save(self):
            self._save_file(os.path.join(self._root_path, self._from_alias))
    
    
    # 储存器工厂
    class SaverFactor:
        def __init__(self, mode: int):
            self.__mode = mode
    
        def __call__(self, root_path, file_name, file_data, email_info: EmailInfo):
            """
                保存模式    SAVE_MODE
            【0:所有附件存入一个文件夹】
            【1:每个邮箱地址一个文件夹】
            【2:每个邮件主题一个文件夹】
            【3:每个发件人的每个邮件主题一个文件夹】
            【4:每个发件人昵称一个文件夹】
            """
            if self.__mode == 0:
                return MergeSaver(root_path, file_name, file_data)
            elif self.__mode == 1:
                return AddressClassifySaver(root_path, file_name, file_data, email_info.from_address)
            elif self.__mode == 2:
                return SubjectClassifySaver(root_path, file_name, file_data, email_info.subject)
            elif self.__mode == 3:
                return AddressSubjectClassifySaver(root_path, file_name, file_data, email_info.from_address,
                                                   email_info.subject)
            elif self.__mode == 4:
                return AliasClassifySaver(root_path, file_name, file_data, email_info.from_name)
            else:
                return None
    
    
    # 邮件属性判断_基类
    class EmailJudge:
        @abc.abstractmethod
        def judge(self):
            pass
    
    
    # 日期判断
    class DateJudge(EmailJudge):
        def __init__(self, date_begin, date_end, time_zone, email_date):
            self.__date_begin = date_begin
            self.__date_end = date_end
            self.__time_zone = time_zone
            self.__email_date = email_date
    
        def judge(self):
            # Date格式'4 Jan 2020 11:59:25 +0800'
            date_mail = datetime.datetime.strptime(self.__email_date, '%d %b %Y %H:%M:%S %z')
            date_begin = datetime.datetime.strptime((self.__date_begin + self.__time_zone), '%Y-%m-%d %H:%M%z')
            date_end = datetime.datetime.strptime((self.__date_end + self.__time_zone), '%Y-%m-%d %H:%M%z')
            return date_begin < date_mail < date_end
    
        @staticmethod
        # 比较是否比Target时间更早,用于结束邮件遍历的循环。包含时区。
        def is_earlier(email_time, target_time):
            email_datetime = datetime.datetime.strptime(email_time, '%d %b %Y %H:%M:%S %z')
            target_datetime = datetime.datetime.strptime(target_time, '%Y-%m-%d %H:%M%z')
            return email_datetime < target_datetime
    
    
    # 邮件主题判断
    class SubjectJudge(EmailJudge):
        def __init__(self, subject_include, email_subject):
            self.__subject_include = subject_include
            self.__email_subject = email_subject
    
        def judge(self):
            return self.__subject_include in self.__email_subject
    
    
    # 邮件发件人地址判断
    class AddressJudge(EmailJudge):
        def __init__(self, address_include, email_from_address):
            self.__address_include = address_include
            self.__email_from_address = email_from_address
    
        def judge(self):
            return self.__address_include in self.__email_from_address
    
    
    # 邮件发件人姓名判断
    class NameJudge(EmailJudge):
        def __init__(self, name_include, email_from_name):
            self.__name_include = name_include
            self.__email_from_name = email_from_name
    
        def judge(self):
            return self.__name_include in self.__email_from_name
    
    
    # 邮件筛选器
    class EmailFilter:
        def __init__(self):
            self.__judges = []
    
        def add_judge(self, judge: EmailJudge):
            self.__judges.append(judge)
    
        def judge_conditions(self):
            for condition_judge in self.__judges:
                if not condition_judge.judge():
                    return False
            return True
    
    
    # 批量邮件下载类
    class BatchEmail:
        def __init__(self, mode, email_server, email_address, email_password):
            self.__save_mode = 0  # 附件保存模式
            self.save_path = 'Email-Attachments'  # 附件保存位置
    
            # 筛选属性
            self.date_begin, self.date_end = '2020-1-1 00:00', '2020-1-4 20:00'  # 筛选属性:起止时间
            self.time_zone = '+0800'  # 筛选属性:时区
            self.from_address = ''  # 筛选属性:发件人地址
            self.from_name = ''  # 筛选属性:发件人姓名
            self.subject = ''  # 筛选属性:邮件主题
    
            self.__saver_factor = None
    
            if mode.lower().find('pop') != -1:
                self.__receiver = Pop3Receiver(email_server, email_address, email_password)
            elif mode.lower().find('imap') != -1:
                self.__receiver = ImapReceiver(email_server, email_address, email_password)
            else:
                print('请选择邮件协议,POP3或IMAP。')
                return
    
        def set_save_mode(self, save_mode):
            self.__save_mode = save_mode
            self.__saver_factor = SaverFactor(self.__save_mode)
    
        def download_attachments(self):
            if self.__receiver is None:
                return
    
            # 邮件数量和总大小:
            mail_quantity, mail_total_size = self.__receiver.get_email_status()
            print('邮件总数:', mail_quantity)
            if mail_total_size > 0:
                print('邮件总大小:', EmailInfo.bytes_to_readable(mail_total_size), end='\n\n')
    
            # mail_list中是各邮件信息,格式['number octets'] (1 octet = 8 bits)
            mail_list = self.__receiver.get_mail_list()
            error_count = 0
    
            # 倒序读取(从最新的开始)
            for mail_number in mail_list:
                # mail_number = '590'     # debug
    
                try:
                    content_byte = self.__receiver.get_mail_header_bytes(mail_number)
                    mail_message = self.parse_mail_byte_content(content_byte)
                    message_info = self.__get_email_info(mail_message)
                except Exception as e:
                    print('邮件接收或解码失败,邮件编号:[%s]  错误信息:%s' % (mail_number, e))
                    error_count += 1
                    continue
    
                email_filter = EmailFilter()
                email_filter.add_judge(DateJudge(self.date_begin, self.date_end, self.time_zone, message_info.date))
                email_filter.add_judge(SubjectJudge(self.subject, message_info.subject))
                email_filter.add_judge(AddressJudge(self.from_address, message_info.from_address))
                email_filter.add_judge(NameJudge(self.from_name, message_info.from_name))
    
                # 超出设定的最早时间则结束循环
                if DateJudge.is_earlier(message_info.date, self.date_begin + self.time_zone):
                    break
    
                if email_filter.judge_conditions():
                    content_byte, message_info.size = self.__receiver.get_full_mail_bytes(mail_number)  # 接收完整邮件
                    mail_message = self.parse_mail_byte_content(content_byte)
                    file_number = self.__save_email_attachments(mail_message, message_info)
    
                    print(
                        '( %d / %d )【%s】' % (
                            len(mail_list) - int(mail_number) + 1, len(mail_list), message_info.subject), end='')
                    print('已保存,下一封') if file_number != 0 else print('无附件')
                    message_info.print_info()
                else:
                    print('( %d / %d )【%s】不符合筛选条件,下一封' % (
                        len(mail_list) - int(mail_number) + 1, len(mail_list), message_info.subject))
            print('处理完成')
            if error_count > 0:
                print('有 %d 个邮件发生错误,请手动检查' % error_count)
    
        def close(self):
            self.__receiver.close()
    
        @staticmethod
        # 将邮件中的bytes数据转为字符串
        def decode_mail_info_str(content):
            result_content = []
            for value, charset in decode_header(content):
                if type(value) != str:
                    if charset is None:
                        value = value.decode(errors='replace')
                    elif charset.lower() in ['gbk', 'gb2312', 'gb18030']:
                        # 一些特殊符号标着gbk,但编码可能是gb18030中的。gb18030向下兼容gbk、gb2312,所以一律用gb18030。
                        value = value.decode(encoding='gb18030', errors='replace')
                    else:
                        value = value.decode(charset, errors='replace')
    
                result_content.append(value)
            return ' '.join(result_content)
    
        @staticmethod
        # 把邮件内容解析为Message对象
        def parse_mail_byte_content(content_byte):
            try:
                mail_content = content_byte.decode()
            except UnicodeDecodeError as e:
                mail_content = content_byte.decode(encoding='GB18030', errors='replace')  # GB18030兼容GB231、GBK
    
            return Parser().parsestr(mail_content)
    
        # 附件解析与保存,返回附件数量
        def __save_email_attachments(self, message: Message, email_info):
            file_count = 0
            for part in message.walk():
                file_name = part.get_filename()
                if file_name:
                    file_name = self.decode_mail_info_str(file_name)
                    email_info.add_attachment_name(file_name)
                    data = part.get_payload(decode=True)
                    self.__saver_factor(self.save_path, file_name, data, email_info).save()
                    file_count += 1
            return file_count
    
        def __get_email_info(self, message: Message):
            email_info = EmailInfo()
    
            try:
                email_info.subject = self.decode_mail_info_str(message.get('Subject'))
            except TypeError as e:
                email_info.subject = '无主题'
    
            name, address = parseaddr(message.get('From'))
            email_info.from_address = address
            email_info.from_name = self.decode_mail_info_str(name)
    
            date = message.get('Date')
            # 少数情况下无Data字段,Received
            if date is None:
                received = message.get('Received')
                if received is None:
                    # 极少数邮件信息头内没有时间信息,偶发于一些系统发送的邮件
                    raise ValueError('该邮件收件时间解析失败,邮件主题:【%s】' % email_info.subject)
                date = received[received.rfind(';') + 1:]
            # Date格式'Sat, 4 Jan 2020 11:59:25 +0800', 也有可能是'4 Jul 2019 21:37:08 +0800'
            # 开头星期去除,部分数据末尾有附加信息,因此以首个冒号后+12截取
            date_begin_index = 0
            for date_begin_index in range(len(date)):
                if '0' <= date[date_begin_index] <= '9':
                    break
            date = date.replace('GMT', '+0000')  # 部分邮件用GMT表示
            email_info.date = date[date_begin_index:date.find(':') + 12]
    
            return email_info
    
    
    # POP3协议 邮件接收类
    class Pop3Receiver:
        def __init__(self, host: str, email_address: str, email_password: str):
            # 连接POP3服务器(SSL):
            try:
                self.__connection = poplib.POP3_SSL(host)
            except OSError as e:
                print('连接服务器失败,请检查服务器地址或网络连接。')
                self.close()
                return
    
            self.__connection.set_debuglevel(False)
            poplib._MAXLINE = 32768  # POP3数据单行最长长度,在有些邮件中,该长度会超出协议建议值,所以适当调高
    
            # 服务器欢迎文字:
            print(self.__connection.getwelcome().decode())
    
            # 登录:
            self.__connection.user(email_address)
            try:
                self.__connection.pass_(email_password)
            except Exception as e:
                print(e.args)
                print('登陆失败,请检查用户名/密码。并确保您的邮箱已开启POP3服务。')
                self.close()
                return
    
        def get_mail_list(self):
            # mail_list中是各邮件信息,格式['number octets'] (1 octet = 8 bits)
            response, mail_list, octets = self.__connection.list()
            return [x.split()[0].decode() for x in reversed(mail_list)]
    
        def get_email_status(self):
            return self.__connection.stat()
    
        def get_mail_header_bytes(self, mail_number: str):
            # TOP命令接收前n行,此处仅读取邮件属性,读部分数据可加快速度。TOP非所有服务器支持,若不支持请使用RETR。
            response, content_byte, octets = self.__connection.top(mail_number, 40)
            # 第一个空行前之是头部信息 RFC822
            try:
                mail_header_end = content_byte.index(b'')
            except ValueError as e:
                mail_header_end = len(content_byte)
            return self.__merge_bytes_list(content_byte[:mail_header_end])
    
        def get_full_mail_bytes(self, mail_number: str):
            response, content_byte, size = self.__connection.retr(mail_number)
            return self.__merge_bytes_list(content_byte), size
    
        @staticmethod
        def __merge_bytes_list(bytes_list):
            # 注:极个别邮件中,同一封邮件存在多种编码,那么就不要join后整体解码,而是每一行单独解码。情况少见,暂时忽略。
            return b'\n'.join(bytes_list)
    
        def close(self):
            if self.__connection is not None:
                try:
                    self.__connection.close()
                except OSError as e:
                    print('断开时发生错误')
                self.__connection = None
    
    
    # IMAP4协议 邮件接收类
    class ImapReceiver:
        def __init__(self, host: str, email_address: str, email_password: str):
            # 连接IMAP4服务器(SSL):
            try:
                self.__connection = imaplib.IMAP4_SSL(host)
            except OSError as e:
                print('连接服务器失败,请检查服务器地址或网络连接。')
                self.close()
                return
    
            # 登录:
            try:
                s = self.__connection.login(email_address, email_password)
            except Exception as e:
                print(e.args)
                print('登陆失败,请检查用户名/密码。并确保您的邮箱已开启IMAP服务。')
                self.close()
                return
    
        def get_email_status(self):
            response, data = self.__connection.status('INBOX', '(MESSAGES)')
            quantity = int(re.findall('\d+', data[0].split()[2].decode())[0])
            return quantity, -1
    
        def get_mail_list(self):
            self.__connection.select()
            response, mail_list = self.__connection.search(None, 'ALL')
            return [x.decode() for x in reversed(mail_list[0].split())]
    
        def get_mail_header_bytes(self, mail_number: str):
            response, data = self.__connection.fetch(mail_number, '(BODY[HEADER])')
            if data[0] is None:
                # 极少数邮件无法获取到内容,一般是系统发送的邮件
                raise ValueError('邮件解析失败')
            return data[0][1]
    
        def get_full_mail_bytes(self, mail_number: str):
            response, data = self.__connection.fetch(mail_number, '(RFC822)')
            size = int(re.findall('\d+', data[0][0].split()[2].decode())[0])
            return data[0][1], size
    
        def close(self):
            if self.__connection is not None:
                try:
                    self.__connection.close()
                except OSError as e:
                    print('断开时发生错误')
                self.__connection = None
    

     

    展开全文
  • python+POP3 批量下载邮件附件

    千次阅读 热门讨论 2018-03-12 19:16:27
    最近新开学,接到了给老板的本科课程当助教的工作,百十来号人一学期下来得有四五次作业发进邮箱里,需要我来统计打分,想想挨个点进去下载附件的过程就头大,于是萌生了写个脚本来统计作业的想法。其实python里收发...

    最近新开学,接到了给老板的本科课程当助教的工作,百十来号人一学期下来得有四五次作业发进邮箱里,需要我来统计打分,想想挨个点进去下载附件的过程就头大,于是萌生了写个脚本来统计作业的想法。

    其实python里收发邮件都有很方便的包,合理使用就好,可以解决绝大多数的邮件收发任务。但是这个脚本写下来还是花了不少时间,其中最大的一部分时间是花在了python的编码问题上,python2和python3的编码预设有些许的不一样,在python3中又取消了unicode这个方法,这就导致很多在python2中写的例程都没法在3中运行。

    经过修改以后下面的脚本可以从邮箱中获取指定日期范围内的邮件附件,并存如指定目录中。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import poplib
    import email
    import datetime
    import time
    import os
    import xlrd
    import xlwt
    from email.parser import Parser
    from email.header import decode_header
    from email.utils import parseaddr
    
    
    
    # 输入邮件地址, 口令和POP3服务器地址:
    email = 'xxxxxxxx@qq.com'
    password = 'xxxxxxxxxxx'
    pop3_server = 'pop.qq.com'
    
    
    
    def decode_str(s):#字符编码转换
        value, charset = decode_header(s)[0]
        if charset:
            value = value.decode(charset)
        return value
    
    
    def get_att(msg):
        import email
        attachment_files = []
        
        for part in msg.walk():
            file_name = part.get_filename()#获取附件名称类型
            contType = part.get_content_type()
            
            if file_name: 
                h = email.header.Header(file_name)
                dh = email.header.decode_header(h)#对附件名称进行解码
                filename = dh[0][0]
                if dh[0][1]:
                    filename = decode_str(str(filename,dh[0][1]))#将附件名称可读化
                    print(filename)
                    #filename = filename.encode("utf-8")
                data = part.get_payload(decode=True)#下载附件
                att_file = open('E:\\L\\zy\\307\\' + filename, 'wb')#在指定目录下创建文件,注意二进制文件需要用wb模式打开
                attachment_files.append(filename)
                att_file.write(data)#保存附件
                att_file.close()
        return attachment_files
    
            
                
    # 连接到POP3服务器,有些邮箱服务器需要ssl加密,对于不需要加密的服务器可以使用poplib.POP3()
    server = poplib.POP3_SSL(pop3_server)
    server.set_debuglevel(1)
    # 打印POP3服务器的欢迎文字:
    print(server.getwelcome().decode('utf-8'))
    # 身份认证:
    server.user(email)
    server.pass_(password)
    # 返回邮件数量和占用空间:
    print('Messages: %s. Size: %s' % server.stat())
    # list()返回所有邮件的编号:
    resp, mails, octets = server.list()
    # 可以查看返回的列表类似[b'1 82923', b'2 2184', ...]
    print(mails)
    index = len(mails)
    
    
    
    for i in range(index,0,-1):
        #倒序遍历邮件
        resp, lines, octets = server.retr(i)
        # lines存储了邮件的原始文本的每一行,
        #邮件的原始文本:
        msg_content = b'\r\n'.join(lines).decode('utf-8')
        #解析邮件:
        msg = Parser().parsestr(msg_content)
        #获取邮件时间
        date1 = time.strptime(msg.get("Date")[0:24],'%a, %d %b %Y %H:%M:%S') #格式化收件时间
        date2 = time.strftime("%Y%m%d", date1)#邮件时间格式转换
        if (date2<'20180306')|(date2>'20180314'):
            
            continue
        f_list = get_att(msg)#获取附件
        
            
        
        #print_info(msg)
    
    server.quit()

    展开全文
  • 工作中常常碰到需要给不同的人发不同的附件,但是内容又相似的情况,比如给合作方发送账单,给员工发工资,用python的 email库,可以方便的发送邮件,参考了一些网上的资料,整理了一个批量发送邮件python脚本 ...
  • python批量解析邮件下载附件

    千次阅读 2018-08-02 16:29:09
    python中的email模块可以方便的解析邮件,先上代码 #-*- encoding: gb2312 -*- import os import email def mail_to_text(mailname,datapath,index): #由于批处理的邮件包含的附件名称相同,这里传入一个index...
  • python批量下载qq邮箱文件

    千次阅读 2019-10-05 11:25:45
    首先邮箱的配置P0P3 配置成功后可以直接在安装有python的win或者linux跑代码,注意修改文件路径和邮箱名以及访问码!!! 代码如下: https://github.com/prograguo/python.git ...
  • 邮箱附件下载 在收到很多人发的邮件时,一个一个的接收附件比较麻烦,考虑使用python实现指定日期邮件附件的下载 首先需要开启邮箱的pop协议,一般在设置里,以QQ邮箱为例,设置后会给个密码,这个就是程序访问邮箱...
  • python批量发送邮件--包括批量不同附件

    万次阅读 热门讨论 2018-08-30 14:42:00
    每个月初都要给每个工长发预付款账单邮件,月中发结算款账单。重复性机械工作。  一个及格线上的程序员,最起码的觉悟就是将重复性的机械工作自动化,于是,在我花了一个多小时,帮她给一部分工长发了一次邮箱后,...
  • 在进行一次培训后,需要收到学员提交的邮件信息,但是由于人员较多,而且每次点开文件保存到当地步骤较为繁琐,人为操作浪费时间还存在着操作失误的可能性,这样就产生进行批量邮件附件的提取并保存在本地的需求...
  • 以前一直是只发送html正文,前两天遇到了发附件的情况,顺道解决了邮件名乱码的问题,记录一下 很多人学习python,不知道从何学起。 很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。 很多已经做...
  • 4.2 邮件附件下载实现 1、依赖的模块 2、核心代码 3、其他 4、异常处理 5、python代码调用测试 4.3 C#内部代码实现 1、保存路径中“选择路径” 2、运行结束后清空控件中输入的数据 3、各控件内容判断和处理...
  • python利用pop3接收邮件以及下载附件(以163邮箱为例) 1、首先需要引入相关的模块,主要就是两个模块:pop3(接收邮件)和email(解析邮件内容) # POP3(Post Office Protocol 3),即邮局协议的第3个版本, #...
  • Python批量提取eml邮件附件

    千次阅读 2019-05-03 20:21:36
    从eml文件中提取附件python get_email_annex.py ./ # -*- coding: utf-8 -*- import os import sys import email def decode_str(s): # 字符编码转换 value, charset = email.header.decode_...
  • 编写python程序实现批量发送邮件(含附件) 二. 步骤 1. 开启QQ邮箱SMTP服务 ① 登录QQ邮箱,点击“设置”: ② 点击“账户”: ③ 开启SMTP服务(可全部开启): ④ 获取授权码: 2. 代码 在这里插入代码片 3....
  • 这个代码的主要作用是可以:批量发送邮件,且免登录邮箱。 原理知识: SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。 ...
  • 大家好,我是早起。在之前的Python办公自动化专题系列文章中,我们已经讲解了如何使用Python读取、收发邮件等多个邮件管理操作,有关Python处理Excel和Word相关的理论与实战...
  • 前几天在公众号搞了一波送书活动,详见福利:免费赠送240本Python教材,该文推送之后,立刻收到了大量的样书申请表,那么接下来的工作就是下载这些邮件附件并汇总信息准备邮寄。对于这样重复性...
  • 能根据关键字和时间段下载指定的邮箱文件:word,pdf,xls.xlxs,zip,rar 二、系统设置 1、邮箱需要开启POP3服务 举例网易邮箱: (1)进入到网易邮箱官网,“登录”进入邮箱。 (2)点击状态栏上方的“设置”,在弹...
  • python 邮件批处理

    2016-01-19 01:56:00
    仅支持imap4,邮件下载,根据时间建立文件夹,将正文和附件分开。另外有的邮箱如163使用的不规范的imap无法正常处理
  • python批量提取eml附件

    2019-04-26 22:41:00
    批量eml文件中提取附件,使用方式如下 代码如下 1 import email 2 import os 3 import sys 4 5 #获取eml附件信息 6 def Get_Annex_Message(FilePath, Annex_Path): 7 global sum 8 try: ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,271
精华内容 508
关键字:

python批量下载邮件附件

python 订阅