第一次实习,什么也不会,公司让写一个自动定时读取邮件的代码,只好自己去网上搜,踩了很多坑,现在终于弄好了,在这里记录一下,也希望能给需要的人一点帮助。

首先就是登陆邮箱并读取邮件,outlook的话一般使用imap连接,如果是qq邮箱可以使用pop3连接,因为pop3连接一般要求账号是数字开头,但是一般邮件如outlook都是字母开头,所以除了qq邮箱其他建议使用imap。我这边的要求是读取未读邮件。所以email_type选择的是UNSEEN,如果是全部读取可以设置为ALL。

# 登陆邮箱并读取原始邮件
def get_mail(email_address, passsword):
    # 选择服务器
    server = imaplib.IMAP4_SSL('outlook.office365.com')
    server.login(email_address, passsword)
    inbox = server.select("INBOX")
    # 搜索匹配的邮件
    email_type, data = server.search(None, "UNSEEN")
    # 邮件列表,使用空格分割得到邮件索引
    msglist = data[0].split()
    if len(msglist) != 0:
        # 最新邮件
        latest = msglist[len(msglist)-1]
        email_type, datas = server.fetch(latest, '(RFC822)')
        # 使用utf-8解码
        text = datas[0][1].decode('utf-8')
        # 转为email.message对象
        message = email.message_from_string(text)
        # 获取未读邮件数量
        server.select()
        email_unseen_count = len(server.search(None, 'UNSEEN')[1][0].split())
        print('未读邮件一共有:', email_unseen_count)
        email_unseen_id_byte = server.search(None, 'UNSEEN')[1][0].split()
        email_unseen_id = []
        for row in email_unseen_id_byte:
            email_unseen_id.append(row.decode('utf-8'))
        print(email_unseen_id)
        return message
    else:
        print("暂无未读邮件")

然后就是解析邮件内容了,将原始邮件转化为可读邮件,邮件的subject或者email中包含的名字都是经过编码后的字符串,要正常显示就必须decode,定义一个decode函数。

# 将原始邮件转化为可读邮件
def decode_str(s):
    value, charset = decode_header(s)[0]
    if charset:
        value = value.decode(charset)
    return value

这里有一个注意的点是导入decode_headers时是从email.header中导入,如果从nntplib中导入会因为格式不对而报错。

import email
import imaplib
from email.utils import parseaddr
from email.header import decode_header #正确
#from nntplib import decode_header     错误

为了防止非utf-8编码的邮件无法显示,定义一个检测邮件编码函数

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

接下来是通过循环遍历来读取邮件内容

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))
'''

上面的代码中被注释掉的是邮件的内容,因为我觉得内容太多不想显示所以就注释掉了,有需要的可以自己去掉注释符号。

最后就是主函数调用上面的函数,输出邮件内容了

if __name__ == "__main__":
    email_addr = "xxxxxxxx@outlook.com"
    password = "xxxxxxxxx"
    messageObject = get_mail(email_addr, password)
    if messageObject:
        msgDate = messageObject["date"]
        print("发送时间:%s" % msgDate)
        res = print_info(messageObject)
        print(res)

email_addr就是邮件的账号,password就是密码,我还设置了一个发送的时间,是对方发送邮件的时间,如果对方是别的国家,时间可能和这边对不上,我也暂时没找到合适的解决办法

给大家看一下结果,这是不显示邮件内容的情况下

python 如何连接outlook邮箱服务器 python读取outlook邮件_字符串

 因为想要更加解放人力,所以设置了定时启动程序,这样每天只要打开定时程序就可以了

#定义tick,执行我们的python读取邮件任务
def tick():
    print("tick! the time is :%s" % datetime.now())
    os.system("python email_imaplib_t.py")


#设置每10分钟运行一次tick,每5分钟睡眠一次
if __name__ == "__main__":
    scheduler = BackgroundScheduler(timezone="Asia/ShangHai")
    scheduler.add_job(tick, 'interval', minutes=10, start_date="2022-4-1 15:00:00")
    scheduler.start()
    print("Press ctrl + {0} to exit".format('Break' if  == 'nt' else 'C'))
    try:
        while True:
            time.sleep(300)
            print(f"sleep!-{datetime.now()}")
    except(KeyboardInterrupt, SystemExit):
        scheduler.shutdown()
        print("exit the job")

结果如下

python 如何连接outlook邮箱服务器 python读取outlook邮件_python_02