使用Python和正则表达式从Outlook邮件中高效提取关键词、文本及URL

聖光之護
发布: 2025-10-31 11:20:23
原创
825人浏览过

使用python和正则表达式从outlook邮件中高效提取关键词、文本及url

本教程详细介绍了如何利用Python自动化从Outlook邮件中提取特定信息。通过集成`win32com.client`库与Outlook交互,并结合强大的正则表达式,我们能够根据预设的父级和子级关键词,从邮件主题和正文中精准匹配并提取关联的段落文本及URL。文章纠正了常见的正则表达式提取错误,提供了一个优化方案,确保数据抓取准确、高效,并支持将结果保存至本地文件。

引言:自动化Outlook邮件内容提取

在日常工作中,我们经常需要从大量的邮件中筛选并提取特定信息,例如包含特定关键词的段落和相关的网页链接。手动操作不仅效率低下,且容易出错。本教程旨在提供一个Python解决方案,利用win32com.client库与Microsoft Outlook进行交互,并通过精细设计的正则表达式,实现对邮件内容的自动化、精准提取。我们将重点关注如何正确构建正则表达式,以解决在复杂文本(如包含多行日文和URL)中匹配特定模式的挑战。

准备工作与配置

在开始之前,请确保您的Python环境中已安装必要的库,并准备好一个配置文件。

  1. 安装pywin32库: 这是Python与Windows COM对象(包括Outlook)交互的关键。
    pip install pywin32
    登录后复制
  2. 配置文件 (config.json): 我们使用JSON文件来管理程序所需的配置参数,如Outlook文件夹名称、输出路径、父级关键词和子级关键词列表。
    {
      "folder_name": "调达プロジェクト",
      "output_file_path": "E:\output",
      "parent_keyword": "meeting",
      "child_keywords": ["土木一式工事", "産業用機器", "事務用品・機器"]
    }
    登录后复制
    • folder_name: Outlook中待搜索的邮件文件夹名称。
    • output_file_path: 提取结果保存的目录。
    • parent_keyword: 用于在邮件主题中初步筛选邮件的关键词。
    • child_keywords: 用于在邮件正文中精确匹配并提取信息的子关键词列表。

连接Outlook并导航邮件文件夹

Python通过win32com.client模块与Outlook应用程序建立连接。首先获取Outlook应用程序实例,然后访问其MAPI命名空间,进而定位到默认的收件箱(或指定文件夹)。

立即学习Python免费学习笔记(深入)”;

import win32com.client
import os
import json
import logging
import re

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

def read_config(config_file):
    """读取JSON配置文件。"""
    with open(config_file, 'r', encoding="utf-8") as f:
        config = json.load(f)
    return config

def get_outlook_folder(folder_name):
    """连接Outlook并获取指定名称的文件夹对象。"""
    try:
        outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
        inbox = outlook.GetDefaultFolder(6) # 6代表收件箱

        # 遍历收件箱下的所有子文件夹,查找目标文件夹
        for folder in inbox.Folders:
            if folder.Name == folder_name:
                logging.info(f"成功找到文件夹: '{folder_name}'")
                return folder
        logging.warning(f"文件夹 '{folder_name}' 未找到。请检查名称或其位置。")
        return None
    except Exception as e:
        logging.error(f"连接Outlook或获取文件夹时发生错误: {e}")
        return None
登录后复制

邮件主题关键词匹配

在获取到目标Outlook文件夹后,我们会遍历其中的每封邮件。首先,根据配置文件中的parent_keyword对邮件主题进行初步筛选。

# ... (接上文代码)

def search_and_save_email(config):
    """根据配置搜索邮件并提取信息。"""
    folder_name = config.get("folder_name", "")
    output_file_path = config.get("output_file_path", "")
    parent_keyword = config.get("parent_keyword", "")
    child_keywords = config.get("child_keywords", [])

    os.makedirs(output_file_path, exist_ok=True) # 确保输出目录存在

    user_folder = get_outlook_folder(folder_name)
    if not user_folder:
        return

    # 编译父级关键词的正则表达式,用于主题匹配
    # 使用re.escape处理关键词中的特殊字符,确保精确匹配
    parent_keyword_pattern = re.compile(r'(?:' + '|'.join(map(re.escape, parent_keyword.split())) + r')', re.IGNORECASE)

    for item in user_folder.Items:
        # 检查邮件主题是否包含父级关键词
        if parent_keyword_pattern.findall(item.Subject):
            logging.info(f"在邮件主题中找到父级关键词: {item.Subject}")
            # ... (后续正文提取逻辑)
        # else:
        #     logging.debug(f"邮件主题 '{item.Subject}' 不包含父级关键词。")
登录后复制

正文内容与URL的精确提取

这是本教程的核心部分,也是原问题中出现逻辑错误的地方。原始代码尝试先根据子关键词找到段落,再从该段落中提取URL。但由于paragraph_text的定义过于狭窄(仅限于关键词所在行),导致URL无法被正确捕获。

原问题分析

原始代码中提取段落的逻辑如下:

一键职达
一键职达

AI全自动批量代投简历软件,自动浏览招聘网站从海量职位中用AI匹配职位并完成投递的全自动操作,真正实现'一键职达'的便捷体验。

一键职达79
查看详情 一键职达
# ...
paragraph_start = body_lower.rfind('
', 0, match.start())
paragraph_end = body_lower.find('
', match.end())
paragraph_text = item.Body[paragraph_start + 1:paragraph_end]
# ...
url_pattern = re.compile(r'http[s]?://S+')
urls = url_pattern.findall(paragraph_text)
登录后复制

这种方法的问题在于,如果子关键词和其关联的URL不在同一行,或者URL在关键词之后的几行,那么paragraph_text就无法包含URL。在提供的邮件示例中,关键词和URL通常是分行显示的,因此需要一个能跨越多行进行匹配的正则表达式。

优化方案:构建复合正则表达式

为了解决上述问题,我们需要一个能够同时捕获子关键词、其周围文本以及后续URL的正则表达式。关键在于允许正则表达式跨越多行进行匹配。

我们将使用以下正则表达式模式: rf'([^ ]*({"|".join(map(re.escape, child_keywords))})[^ ]*).*?(https?://S+)'

让我们分解这个模式:

  • ([^ ]*({"|".join(map(re.escape, child_keywords))})[^ ]*): 这是一个捕获组,用于匹配包含子关键词的整行文本。
    • [^ ]*: 匹配任意数量的非换行符,代表关键词前后的内容。
    • ({"|".join(map(re.escape, child_keywords))}): 这是一个嵌套的捕获组,动态生成所有子关键词的“或”匹配模式(例如 (关键词A|关键词B|关键词C))。re.escape用于转义关键词中的特殊字符。
  • .*?: 这是一个非贪婪匹配模式,匹配任意字符(包括换行符,因为我们将使用re.S标志),直到遇到下一个模式。这使得它能够跨越多行。
  • (https?://S+): 这是一个捕获组,用于匹配URL。
    • : 单词边界,确保URL是独立的。
    • https?://: 匹配http://或https://。
    • S+: 匹配一个或多个非空白字符,捕获完整的URL。

re.S (re.DOTALL) 标志的重要性: 当使用re.S标志时,正则表达式中的.(点号)将匹配包括换行符在内的所有字符。这对于跨越多行提取信息至关重要,因为它允许.*?模式捕获关键词和URL之间的所有内容,无论中间有多少换行符。

使用re.findall进行批量提取

结合这个优化后的正则表达式和re.findall函数,我们可以一次性从邮件正文中提取所有匹配的子关键词、关联文本和URL。

# ... (接上文代码)

            # 初始化用于存储结果的字符串
            output_text = ""

            # 编译子关键词和URL的复合正则表达式
            # re.escape 处理关键词中的特殊字符
            # re.S (re.DOTALL) 使 '.' 匹配包括换行符在内的所有字符
            child_keyword_url_pattern = re.compile(
                rf'([^
]*({"|".join(map(re.escape, child_keywords))})[^
]*).*?(https?://S+)',
                re.IGNORECASE | re.S
            )

            # 在邮件正文中查找所有匹配项
            # re.findall 返回一个列表,每个元素是一个元组,包含所有捕获组的内容
            matches = child_keyword_url_pattern.findall(item.Body)

            if not matches:
                logging.warning(f"在邮件正文 '{item.Subject}' 中未找到任何子关键词及其关联的URL。")
                continue # 跳过当前邮件,处理下一封

            for match_group in matches:
                # match_group 的结构为 (完整的段落文本, 子关键词, URL)
                # 例如: ('01 事務用品・機器', '事務用品・機器', 'https://...')

                # 提取捕获组内容
                paragraph_text_full = match_group[0].strip() # 包含关键词的整行文本
                found_child_keyword = match_group[1].strip() # 匹配到的子关键词
                found_url = match_group[2].strip() # 匹配到的URL

                logging.info(f"提取到: 关键词='{found_child_keyword}', 文本='{paragraph_text_full}', URL='{found_url}'")

                # 格式化结果
                output_text += f"Child Keyword: {found_child_keyword}
"
                output_text += f"Paragraph Text: {paragraph_text_full}
"
                output_text += f"URLs: {found_url}

"

            # 保存结果到文件
            # 使用邮件主题作为文件名,并替换掉不适合作为文件名的字符
            sanitized_subject = re.sub(r'[\/:*?"<>|]', '_', item.Subject)
            output_file = os.path.join(output_file_path, f"{sanitized_subject}.txt")
            with open(output_file, 'w', encoding='utf-8') as f:
                f.write(output_text)
            logging.info(f"结果已保存至: {output_file}")
        # else:
        #     logging.debug(f"邮件主题 '{item.Subject}' 不包含父级关键词。")
登录后复制

完整的Python实现

将上述所有代码片段整合,并包含主执行逻辑,构成一个完整的Python脚本。

import win32com.client
import os
import json
import logging
import re

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

def read_config(config_file):
    """
    读取JSON配置文件。

    Args:
        config_file (str): 配置文件的路径。

    Returns:
        dict: 包含配置信息的字典。
    """
    with open(config_file, 'r', encoding="utf-8") as f:
        config = json.load(f)
    return config

def get_outlook_folder(folder_name):
    """
    连接Outlook并获取指定名称的文件夹对象。

    Args:
        folder_name (str): Outlook中待搜索的文件夹名称。

    Returns:
        object: Outlook文件夹对象,如果未找到则返回None。
    """
    try:
        outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
        inbox = outlook.GetDefaultFolder(6) # 6代表收件箱

        # 遍历收件箱下的所有子文件夹,查找目标文件夹
        for folder in inbox.Folders:
            if folder.Name == folder_name:
                logging.info(f"成功找到Outlook文件夹: '{folder_name}'")
                return folder
        logging.warning(f"Outlook文件夹 '{folder_name}' 未找到。请检查名称或其位置。")
        return None
    except Exception as e:
        logging.error(f"连接Outlook或获取文件夹时发生错误: {e}")
        return None

def search_and_save_email(config):
    """
    根据配置文件搜索Outlook邮件,提取关键词、关联文本和URL,并保存结果。

    Args:
        config (dict): 包含配置信息的字典。
    """
    try:
        folder_name = config.get("folder_name", "")
        output_file_path = config.get("output_file_path", "")
        parent_keyword = config.get("parent_keyword", "")
        child_keywords = config.get("child_keywords", [])

        # 确保输出目录存在
        os.makedirs(output_file_path, exist_ok=True)

        user_folder = get_outlook_folder(folder_name)
        if not user_folder:
            return

        # 编译父级关键词的正则表达式,用于主题匹配
        # 使用re.escape处理关键词中的特殊字符,确保精确匹配
        parent_keyword_pattern = re.compile(r'(?:' + '|'.join(map(re.escape, parent_keyword.split())) + r')', re.IGNORECASE)

        # 编译子关键词和URL的复合正则表达式
        # re.escape 处理关键词中的特殊字符
        # re.S (re.DOTALL) 使 '.' 匹配包括换行符在内的所有字符
        child_keyword_url_pattern = re.compile(
            rf'([^
]*({"|".join(map(re.escape, child_keywords))})[^
]*).*?(https?://S+)',
            re.IGNORECASE | re.S
        )

        for item in user_folder.Items:
            # 检查邮件主题是否包含父级关键词
            if parent_keyword_pattern.findall(item.Subject):
                logging.info(f"在邮件主题中找到父级关键词: {item.Subject}")

                output_text = "" # 初始化用于存储结果的字符串

                # 在邮件正文中查找所有匹配项
                # re.findall 返回一个列表,每个元素是一个元组,包含所有捕获组的内容
                matches = child_keyword_url_pattern.findall(item.Body)

                if not matches:
                    logging.warning(f"在邮件正文 '{item.Subject}' 中未找到任何子关键词及其关联的URL。")
                    continue # 跳过当前邮件,处理下一封

                for match_group in matches:
                    # match_group 的结构为 (完整的段落文本, 子关键词, URL)
                    paragraph_text_full = match_group[0].strip() # 包含关键词的整行文本
                    found_child_keyword = match_group[1].strip() # 匹配到的子关键词
                    found_url = match_group[2].strip() # 匹配到的URL

                    logging.info(f"提取到: 关键词='{found_child_keyword}', 文本='{paragraph_text_full}', URL='{found_url}'")

                    # 格式化结果
                    output_text += f"Child Keyword: {found_child_keyword}
"
                    output_text += f"Paragraph Text: {paragraph_text_full}
"
                    output_text += f"URLs: {found_url}

"

                # 保存结果到文件
                # 使用邮件主题作为文件名,并替换掉不适合作为文件名的字符
                sanitized_subject = re.sub(r'[\/:*?"<>|]', '_', item.Subject)
                output_file = os.path.join(output_file_path, f"{sanitized_subject}.txt")
                with open(output_file, 'w', encoding='utf-8') as f:
                    f.write(output_text)
                logging.info(f"结果已保存至: {output_file}")
            # else:
            #     logging.debug(f"邮件主题 '{item.Subject}' 不包含父级关键词。")

    except Exception as e:
        logging.error(f"在 search_and_save_email 函数中发生错误: {e}")

if __name__ == "__main__":
    # 指定配置文件的路径
    config_file_path = "E:\config.json" # 请根据实际路径修改

    # 检查配置文件是否存在
    if not os.path.exists(config_file_path):
        logging.error(f"配置文件 '{config_file_path}' 不存在。请创建或修改路径。")
    else:
        # 读取配置
        config = read_config(config_file_path)
        # 执行搜索和保存操作
        search_and_save_email(config)
登录后复制

注意事项与最佳实践

  1. Outlook安全性提示: 当Python脚本尝试访问Outlook时,可能会弹出安全警告,提示某个程序正在尝试访问您的Outlook数据。这是正常的,您需要手动允许访问。为了避免频繁提示,可以在Outlook的安全设置中进行调整,但这通常不推荐用于生产环境,除非您完全信任该脚本。
  2. 正则表达式的性能与复杂度: 复杂的正则表达式可能会影响性能,尤其是在处理大量邮件或超长邮件正文时。本教程中的复合正则表达式已针对效率进行了优化,但仍需注意其在极端情况下的表现。
  3. 错误处理和日志记录: 代码中加入了try-except块和logging模块,这对于识别和调试问题至关重要。在生产环境中,应进一步完善错误处理机制,例如将错误信息记录到文件中,或在发生严重错误时发送通知。
  4. 字符编码 处理包含日文等非ASCII字符的文本时,务必指定正确的编码(如encoding="utf-8"),以避免乱码问题。在读取配置文件和写入输出文件时,本代码已使用UTF-8编码。
  5. 父级关键词的灵活性: 示例中的parent_keyword是单个字符串,parent_keyword.split()会将其分割成单词。如果父级关键词本身是短语,请确保其匹配逻辑符合预期。例如,如果parent_keyword是"project meeting",split()会得到["project", "meeting"],正则会匹配包含"project"或"meeting"的邮件。若需精确匹配整个短语,应调整parent_keyword_pattern的构建方式。
  6. URL匹配的精确性: 示例中的URL匹配模式https?://S+可以捕获大多数常见URL。如果需要更严格或更宽松的URL匹配规则,可以进一步修改正则表达式。

总结

通过本教程,我们学习了如何利用Python的win32com.client库与Outlook进行深度集成,并运用强大的正则表达式从邮件正文中精确提取关键词、关联文本和URL。关键在于构建一个能够跨越多行捕获所需信息的复合正则表达式,并配合re.S标志。这个解决方案不仅提高了数据提取的效率和准确性,也

以上就是使用Python和正则表达式从Outlook邮件中高效提取关键词、文本及URL的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号