Python中如何实现邮件实时收取功能?

用 python 写了个下载邮件附件的脚本,在 windows 计划任务里做了个 1 分钟循环定时。有没有办法改成实时接收?如果写 while 循环的话,一直重复登录邮箱是不是不太好,能否实现监听邮箱有新邮件再运行


Python中如何实现邮件实时收取功能?
25 回复

IMAP POP3 都没有推送,只能定时查询
Exchange ActiveSync 了解下


import imaplib
import email
from email.header import decode_header
import time

class EmailMonitor:
    def __init__(self, username, password, imap_server='imap.gmail.com'):
        self.username = username
        self.password = password
        self.imap_server = imap_server
        self.mail = None
        self.last_uid = 0  # 记录最后处理的邮件UID
        
    def connect(self):
        """建立IMAP连接"""
        self.mail = imaplib.IMAP4_SSL(self.imap_server)
        self.mail.login(self.username, self.password)
        self.mail.select('INBOX')
        
    def get_new_emails(self):
        """获取新邮件"""
        # 搜索所有未读邮件
        status, messages = self.mail.search(None, 'UNSEEN')
        if status != 'OK':
            return []
            
        email_ids = messages[0].split()
        new_emails = []
        
        for e_id in email_ids:
            # 获取邮件内容
            status, msg_data = self.mail.fetch(e_id, '(RFC822)')
            if status != 'OK':
                continue
                
            # 解析邮件
            raw_email = msg_data[0][1]
            msg = email.message_from_bytes(raw_email)
            
            # 解码邮件主题
            subject, encoding = decode_header(msg['Subject'])[0]
            if isinstance(subject, bytes):
                subject = subject.decode(encoding if encoding else 'utf-8')
                
            # 获取发件人
            from_ = msg.get('From')
            
            # 获取邮件正文
            body = ''
            if msg.is_multipart():
                for part in msg.walk():
                    content_type = part.get_content_type()
                    content_disposition = str(part.get('Content-Disposition'))
                    
                    if content_type == 'text/plain' and 'attachment' not in content_disposition:
                        body = part.get_payload(decode=True).decode()
                        break
            else:
                body = msg.get_payload(decode=True).decode()
            
            new_emails.append({
                'id': e_id.decode(),
                'subject': subject,
                'from': from_,
                'body': body[:200]  # 只取前200字符
            })
            
        return new_emails
    
    def mark_as_read(self, email_id):
        """标记邮件为已读"""
        self.mail.store(email_id, '+FLAGS', '\\Seen')
    
    def monitor_loop(self, interval=30):
        """监控循环"""
        print(f"开始监控邮箱: {self.username}")
        print("按 Ctrl+C 停止监控\n")
        
        try:
            while True:
                try:
                    new_emails = self.get_new_emails()
                    
                    if new_emails:
                        print(f"发现 {len(new_emails)} 封新邮件:")
                        for email_data in new_emails:
                            print(f"\n主题: {email_data['subject']}")
                            print(f"发件人: {email_data['from']}")
                            print(f"内容预览: {email_data['body']}")
                            print("-" * 50)
                            
                            # 标记为已读
                            self.mark_as_read(email_data['id'])
                    
                    time.sleep(interval)
                    
                except Exception as e:
                    print(f"错误: {e}")
                    # 尝试重新连接
                    time.sleep(5)
                    self.connect()
                    
        except KeyboardInterrupt:
            print("\n停止监控")
        finally:
            self.disconnect()
    
    def disconnect(self):
        """断开连接"""
        if self.mail:
            self.mail.close()
            self.mail.logout()

# 使用示例
if __name__ == "__main__":
    # 配置你的邮箱信息
    monitor = EmailMonitor(
        username='your_email@gmail.com',
        password='your_app_password'  # 使用应用专用密码
    )
    
    # 连接邮箱
    monitor.connect()
    
    # 开始监控,每30秒检查一次
    monitor.monitor_loop(interval=30)

核心要点:

  1. 使用imaplib库实现IMAP协议连接
  2. 通过search('UNSEEN')查找未读邮件
  3. 循环检查新邮件并解析内容
  4. 使用应用专用密码而非普通密码(Gmail需要)

注意替换your_email@gmail.comyour_app_password为实际值。对于Gmail,需要在设置中启用IMAP并生成应用专用密码。

一句话总结:用imaplib轮询检查UNSEEN邮件就行。

一直登录有啥不好?

其实 foxmail 这种也是定时去刷新登录的

邮件服务商没有提供可以推送的协议的话,就只能轮询了

自建邮件服务器就可以实时 其他登录后抓取就是了

你开着 Outlook 2016 然后用 Outlook object model 检查新邮件就行了。

自动转发到 sendcloud 或者 mailgun,然后创建一个收信路由。

首先 SMTP 不是 realtime 协议,所以我觉得实时收取这个需求本身是不存在的。
一定要尽量快的话,IMAP 有 Push 扩展可以试试(效果也就那样)

前端轮询, 或者后台轮询然后主动推给前端

小秘密:SMTP 甚至不是一个收信协议!

另一个小秘密:所有使用 Exchange 的用户都是实时收取的。

Exchange Active Sync, Mapi 系列可以

EWS 也要手动

楼上已经说了,传统 Email 服务的协议 SMTP、POP3、IMAP 都是没有推送这一块的,所以你只能轮询。至于为什么这样设计,因为普通邮递就是这样的,不加钱就只送到邮箱。

我用的方法可能不高大上,但比较实用吧

直接转发到 QQ 邮箱,然后来邮件了微信就会马上滴滴滴

我用 GMail 收信比较及时,只慢一两分钟的样子,你自己写程序话,可以找找有 api 提供邮箱吧

SMTP 啊(如果你有公网 IP ),要知道以前的时候 sendmail 都是 Unix 发行版的标配。

另外 fetchmail 不了解一下?曾经电子邮件界的明星程序



SMTP 怎么不是收信协议了。SMTP 是简单邮件“传输”协议,传输包括收和发两部分!
msa 和 mta 前者发后者收和发,都是 SMTP 的实现。
当然不是收到信箱里,收到信箱里不叫收,叫“投递”

设置自动转发到自己服务器

#18 除非你是邮件服务器,目前我不 aware of 任何用 SMTP 收邮件的客户端。

但作为邮件服务器不存在“ SMTP 不实时”这种说法(它根本不 make sense ),因为邮件到达服务器才算收到,或者说服务器收到邮件的时刻才定义了这封邮件的“实时”。

#20

对啊,所以说 SMTP 并不能说“不是收信协议”。它是传输协议,既发也收

使用 SMTP 收信的客户端很多啊,MTA 都是啊,以前用的最多的就是 sendmail。这东西还是曾经很多 Unix 系统的标配。

怎么开?

我用的就是 Exchange,但 outlook 收信一直晚于网页是怎么回事,收集间隔设置的最小 1 分钟

一般实时的我都用 queue.get()
线程循环 queue.put()
你听得懂我在说什么吗?

回到顶部