You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python界面中正确显示Outlook邮件的内嵌图片与签名图片?

问题解决:Outlook内嵌图片无法显示的修复方案

问题分析

你遇到的核心问题是:普通Outlook邮件正文、签名中的内嵌图片无法加载(显示损坏链接),仅全HTML营销邮件的图片正常。现有代码仅处理了Type=6的内嵌附件,但普通邮件的内嵌图片可能不符合这个判断条件,同时存在以下几个问题:

  1. 未初始化temp_dir变量,导致图片保存失败
  2. 硬编码图片MIME类型为image/jpeg,无法适配png、gif等格式
  3. 未处理cid带尖括号(如<image001.png@01D9XXXX>)的情况
  4. 普通Outlook邮件的内嵌图片可能不标记为Type=6,需通过PR_ATTACH_CONTENT_ID属性判断

修复步骤

1. 初始化临时目录

在代码开头添加临时目录创建逻辑,避免路径错误:

temp_dir = tempfile.mkdtemp()

2. 改进内嵌图片识别逻辑

不再仅依赖Attachment.Type == 6,而是检查是否存在PR_ATTACH_CONTENT_ID属性(即cid),这样能覆盖更多内嵌图片场景:

def save_inline_images(message):
    inline_images = {}
    attachments = message.Attachments

    for attachment in attachments:
        try:
            # 尝试获取cid,存在则视为内嵌图片
            cid = attachment.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001F")
            if cid:
                # 处理带尖括号的cid
                cid = cid.strip('<>')
                file_path = os.path.join(temp_dir, attachment.FileName)
                attachment.SaveAsFile(file_path)
                
                # 获取图片MIME类型,避免硬编码
                mime_type = attachment.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x370E001F") or "image/jpeg"
                
                with open(file_path, "rb") as f:
                    image_data = f.read()
                    encoded_image = base64.b64encode(image_data).decode("utf-8")
                inline_images[cid] = (mime_type, encoded_image)
        except Exception as e:
            # 忽略无cid的普通附件
            continue

    return inline_images

3. 动态替换图片src

根据实际MIME类型生成data URI,同时处理HTML中cid带尖括号的情况:

def replace_inline_images(html_body, inline_images):
    soup = BeautifulSoup(html_body, 'html.parser')

    for img in soup.find_all('img'):
        src = img.get('src', '')
        if src.startswith('cid:'):
            cid = src[4:]
            # 处理HTML中cid带尖括号的情况
            cid = cid.strip('<>')
            if cid in inline_images:
                mime_type, encoded_image = inline_images[cid]
                img['src'] = f"data:{mime_type};base64,{encoded_image}"

    return str(soup)

4. 清理临时目录(可选)

在程序退出时删除临时目录,避免残留文件:

import atexit
atexit.register(lambda: os.rmdir(temp_dir))

完整修复代码

import sys
import os
import tempfile
import atexit
from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QListWidget, QListWidgetItem, QDialog
from PyQt6.QtWebEngineWidgets import QWebEngineView
import win32com.client
from bs4 import BeautifulSoup
import base64

# 创建临时目录并注册清理
temp_dir = tempfile.mkdtemp()
atexit.register(lambda: os.rmdir(temp_dir))

# Function to fetch emails from Outlook
def fetch_emails():
    try:
        outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
        inbox = outlook.GetDefaultFolder(6)  # 6 refers to the inbox folder
        messages = inbox.Items
        messages.Sort("[ReceivedTime]", True)

        emails = []
        for i in range(min(10, len(messages))):  # Fetching last 10 emails
            message = messages[i]
            try:
                # Check if HTMLBody is available and not empty
                if hasattr(message, 'HTMLBody') and message.HTMLBody:
                    html_body = str(message.HTMLBody)  # Convert HTMLBody to string
                    inline_images = save_inline_images(message)
                    html_body = replace_inline_images(html_body, inline_images)

                    emails.append({
                        'subject': message.Subject,
                        'html_body': html_body,
                        'datetime_received': message.ReceivedTime,
                    })
                else:
                    emails.append({
                        'subject': message.Subject,
                        'html_body': '<p>This email does not contain HTML content.</p>',
                        'datetime_received': message.ReceivedTime,
                    })
                    print(f"Email {i} ('{message.Subject}') does not have an HTMLBody.")
            except Exception as e:
                print(f"An error occurred while accessing email {i} ('{message.Subject}'): {e}")
                continue
        return emails
    except Exception as e:
        print(f"An error occurred: {e}")
        return []

# Function to save inline images from an email
def save_inline_images(message):
    inline_images = {}
    attachments = message.Attachments

    for attachment in attachments:
        try:
            # 获取cid,存在则视为内嵌图片
            cid = attachment.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001F")
            if cid:
                # 移除cid可能带的尖括号
                cid = cid.strip('<>')
                file_path = os.path.join(temp_dir, attachment.FileName)
                attachment.SaveAsFile(file_path)
                
                # 获取图片的MIME类型,适配多种格式
                mime_type = attachment.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x370E001F") or "image/jpeg"
                
                with open(file_path, "rb") as f:
                    image_data = f.read()
                    encoded_image = base64.b64encode(image_data).decode("utf-8")
                inline_images[cid] = (mime_type, encoded_image)
        except Exception as e:
            # 跳过无cid的普通附件
            continue

    return inline_images

# Function to replace inline images in the HTML content
def replace_inline_images(html_body, inline_images):
    soup = BeautifulSoup(html_body, 'html.parser')

    for img in soup.find_all('img'):
        src = img.get('src', '')
        if src.startswith('cid:'):
            cid = src[4:]
            # 移除HTML中cid的尖括号
            cid = cid.strip('<>')
            if cid in inline_images:
                mime_type, encoded_image = inline_images[cid]
                img['src'] = f"data:{mime_type};base64,{encoded_image}"

    return str(soup)

class EmailViewer(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Outlook Email Viewer")
        self.setGeometry(100, 100, 800, 600)

        # Main layout
        main_layout = QVBoxLayout()
        main_widget = QWidget()
        main_widget.setLayout(main_layout)
        self.setCentralWidget(main_widget)

        # Email list
        self.email_list = QListWidget()
        main_layout.addWidget(self.email_list)

        # Fetch and display emails
        self.emails = fetch_emails()
        if not self.emails:
            self.email_list.addItem(QListWidgetItem("No emails to display"))

        for email in self.emails:
            item = QListWidgetItem(f"{email['subject']} ({email['datetime_received']})")
            self.email_list.addItem(item)

        # Connect list item click to email display
        self.email_list.itemClicked.connect(self.open_email_viewer)

    def open_email_viewer(self, item):
        index = self.email_list.row(item)
        email = self.emails[index]

        # Create a new window to display the email
        self.email_window = QDialog(self)
        self.email_window.setWindowTitle(email['subject'])
        self.email_window.setGeometry(150, 150, 800, 600)

        # Layout for the new window
        email_layout = QVBoxLayout()
        self.email_window.setLayout(email_layout)

        # QWebEngineView to display the email content
        email_viewer = QWebEngineView()
        email_layout.addWidget(email_viewer)
        email_viewer.setHtml(email['html_body'])

        self.email_window.exec()

# Run the application
if __name__ == "__main__":
    app = QApplication(sys.argv)
    viewer = EmailViewer()
    viewer.show()
    sys.exit(app.exec())

关键修改说明

  • 临时目录:创建并自动清理,避免路径错误和文件残留
  • 图片识别:通过PR_ATTACH_CONTENT_ID判断内嵌图片,覆盖普通邮件和签名图片场景
  • MIME类型动态获取:适配jpeg、png、gif等多种图片格式
  • cid处理:移除可能存在的尖括号,确保HTML中的cid和附件cid匹配

内容的提问来源于stack exchange,提问作者Joshua S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 05:29:54