Skip to main content

Python 电子邮件

Python IMAP 收取邮件

本章节主要提供一个完整的 Python IMAP 收取邮件示例,使用内置模块 imaplib 和 email 接收邮件,包含错误处理、附件支持和多部分邮件解析,适用于大多数邮箱服务。只需修改 imap 服务器地址,用户邮箱账号即可。

代码功能:

  • 连接 IMAP 服务器,获取最新 5 封邮件(可通过 max_emails 调整)。
  • 解析发件人、主题、日期、正文(优先纯文本,备选 HTML)。
  • 支持附件下载(保存到 attachments 文件夹,需设置 save_attachments=True)。
  • 包含错误处理,确保代码健壮。
import imaplib
import email
from email.header import decode_header
import os
from datetime import datetime

# qq 邮箱配置
EMAIL = "xxxxxx@qq.com"
PASSWORD = "xxxxxx"  # 客户端授权码
IMAP_SERVER = "imap.qq.com"  # 使用 imap 而不是 pop3


def fetch_emails(email_address, password, imap_server, max_emails=5, save_attachments=False):
    try:
        # 连接到 IMAP 服务器
        mail = imaplib.IMAP4_SSL(imap_server, timeout=30)
        mail.login(email_address, password)
        mail.select("inbox", readonly=True)

        # 搜索所有邮件
        _, data = mail.search(None, "ALL")
        email_ids = data[0].split()[-max_emails:]  # 获取最新的 max_emails 封邮件

        emails = []
        for email_id in email_ids:
            # 获取邮件内容
            _, msg_data = mail.fetch(email_id, "(RFC822)")
            email_body = msg_data[0][1]
            msg = email.message_from_bytes(email_body)

            # 解析邮件主题
            subject, encoding = decode_header(msg["subject"])[0]
            if isinstance(subject, bytes):
                subject = subject.decode(encoding or "utf-8", errors="ignore")

            # 解析发件人和日期
            from_ = msg.get("from")
            date_ = msg.get("date")
            try:
                date_ = datetime.strptime(date_, "%a, %d %b %Y %H:%M:%S %z").strftime("%Y-%m-%d %H:%M:%S")
            except (ValueError, TypeError):
                date_ = date_ or "Unknown Date"

            # 解析邮件正文
            body = ""
            attachments = []
            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:
                        try:
                            body = part.get_payload(decode=True).decode(errors="ignore")
                        except:
                            body = "Unable to decode body"
                    elif content_type == "text/html" and not body:
                        try:
                            body = part.get_payload(decode=True).decode(errors="ignore")
                        except:
                            body = "Unable to decode body"

                    # 提取附件
                    if "attachment" in content_disposition and save_attachments:
                        filename = part.get_filename()
                        if filename:
                            filename, encoding = decode_header(filename)[0]
                            if isinstance(filename, bytes):
                                filename = filename.decode(encoding or "utf-8", errors="ignore")
                            filepath = os.path.join("attachments", filename)
                            os.makedirs("attachments", exist_ok=True)
                            with open(filepath, "wb") as f:
                                f.write(part.get_payload(decode=True))
                            attachments.append(filepath)

            else:
                # 非多部分邮件
                try:
                    body = msg.get_payload(decode=True).decode(errors="ignore")
                except:
                    body = "Unable to decode body"

            emails.append({
                "id": email_id.decode(),
                "from": from_,
                "subject": subject,
                "date": date_,
                "body": body[:500] + ("..." if len(body) > 500 else ""),
                "attachments": attachments
            })

        # 关闭连接
        mail.logout()
        return emails

    except Exception as e:
        print(f"Error: {str(e)}")
        return []


def main():
    # 获取邮件
    emails = fetch_emails(EMAIL, PASSWORD, IMAP_SERVER, max_emails=5, save_attachments=True)

    # 打印邮件信息
    for email in emails:
        print(f"\nEmail ID: {email['id']}")
        print(f"From: {email['from']}")
        print(f"Subject: {email['subject']}")
        print(f"Date: {email['date']}")
        print(f"Body: {email['body']}")
        if email['attachments']:
            print(f"Attachments: {', '.join(email['attachments'])}")
        print("-" * 100)


if __name__ == "__main__":
    main()