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

基于NLP的通用Python邮件解析方案求助

邮件解析通用化解决方案

问题背景

现有Python邮件解析代码仅适配单封邮件,通用性不足。使用正则表达式提取信息时,在以下场景存在瓶颈:

  • 无法适配任意格式的地址提取
  • 难以精准提取特定人员名称(如指定医生姓名、邮件发件实体名称)
  • 处理多日期场景(例如“您的预约从15/01调整至17/01”,需提取第二个日期)时逻辑失效

用户原有代码

search_criteria = 'TEXT "Cityville Medical Clinic"'
status, email_ids = mail.search(None, search_criteria)

# Get the latest email (assuming it's the most relevant)
latest_email_id = email_ids[0].split()[-1]
status, email_data = mail.fetch(latest_email_id, '(RFC822)')

# Extract the email text from the email message
email_message = email.message_from_bytes(email_data[0][1])
email_text = ''
for part in email_message.walk():
    if part.get_content_type() == "text/plain":
        email_text += part.get_payload()

# Load an NLP model (e.g., spaCy)
nlp = spacy.load("en_core_web_sm")
 
# Define regular expressions for extracting mobile numbers and dates

# Initialize lists to store extracted information
names = []
sender_names = []
sender_addresses = []
doctor_names = []
dates = []
times = []
addresses = []
mobile_numbers = []

# Extract information using spaCy and regex
doc = nlp(email_text)
# Extract sender addresses
mobile_pattern = r'(\d{10}|\(\d{3}\)\s*\d{3}-\d{4})'
date_pattern =r'(?:January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{1,2}(?:st|nd|rd|th)?,\s+\d{4}|' \
              r'\d{1,2}(?:st|nd|rd|th)?\s+(?:January|February|March|April|May|June|July|August|September|October|November|December),\s+\d{4}|' \
              r'\d{1,2}/\d{1,2}/\d{4}'

time_pattern = r'\d{1,2}:\d{2}\s+[APap][Mm]'

# Define patterns for extracting specific information
#LOOK_HERE
name_pattern = r'Dear\s+(\w+)'


# Define patterns for extracting sender names and sender addresses
sender_name_pattern = r"Best regards,[\n\s]+([^\n]+)"
sender_address_pattern = r"Best regards,[\n\s]+([^\n]+)[\n\s]+([^\n]+)[\n\s]+([^\n]+)"
#LOOK HERE

doctor_name_pattern = r'with\s+(Dr\.\s+\w+)'
clinic_address_pattern = r'(\d+\s+[A-Za-z\s]+\s*,?\s*[A-Za-z\s]+\s*,?\s*\d{5})'
 
# Extract information using regex
name_match = re.search(name_pattern, email_text)
sender_name_match = re.search(sender_name_pattern, email_text)
sender_address_match = re.search(sender_address_pattern, email_text)
doctor_name_match = re.search(doctor_name_pattern, email_text)
clinic_address_match = re.search(clinic_address_pattern, email_text)
dates = re.findall(date_pattern, email_text)
times = re.findall(time_pattern, email_text)
mobile_numbers = re.findall(mobile_pattern, email_text)

# Print the extracted information
print("recipientName:", name_match.group(1) if name_match else "Not Found")
print("Sender Name:", sender_name_match.group(1) if sender_name_match else "Not Found")
print("Sender Address:", sender_address_match.group(2) if sender_address_match else "Not Found")
print("Doctor Name:", doctor_name_match.group(1) if doctor_name_match else "Not Found")
print("Clinic Address:", clinic_address_match.group(1) if clinic_address_match else "Not Found")
print("Appointment Date:", dates[0] if dates else "Not Found")
print("Appointment Time:", times[0] if times else "Not Found")
print("Mobile Number:", mobile_numbers[0] if mobile_numbers else "Not Found")

通用实现思路与代码改进

核心优化方向

  • 批量处理多邮件:遍历所有匹配的邮件ID,而非仅取最新一封
  • NLP为主+正则为辅:用spaCy的实体识别(NER)处理地址、人名,正则补充场景化规则
  • 场景化规则定制:针对多日期、特定身份(医生)设计精准匹配逻辑
  • 结构化存储:用字典存储每封邮件的提取结果,便于后续处理

改进后代码

import re
import spacy
import email

def parse_single_email(email_data):
    # 提取邮件文本(处理编码解码)
    email_message = email.message_from_bytes(email_data[0][1])
    email_text = ''
    for part in email_message.walk():
        if part.get_content_type() == "text/plain":
            payload = part.get_payload(decode=True)
            charset = part.get_content_charset() or 'utf-8'
            email_text += payload.decode(charset)
    
    nlp = spacy.load("en_core_web_sm")
    doc = nlp(email_text)
    
    # 初始化结构化结果
    result = {
        "recipient_name": "Not Found",
        "sender_name": "Not Found",
        "sender_address": "Not Found",
        "doctor_names": [],
        "clinic_addresses": [],
        "appointment_dates": [],
        "final_appointment_date": "Not Found",
        "appointment_times": [],
        "mobile_numbers": []
    }
    
    # 1. 提取收件人姓名(匹配Dear开头格式)
    name_match = re.search(r'Dear\s+([A-Za-z\s]+?)(?:,|$)', email_text)
    if name_match:
        result["recipient_name"] = name_match.group(1).strip()
    
    # 2. 解析签名区提取发件人信息
    signature_match = re.search(r'(?:Best regards|Sincerely|Regards),\s*([\s\S]+?)(?=\n\n|\Z)', email_text)
    if signature_match:
        signature_text = signature_match.group(1)
        # 提取签名第一行作为发件人姓名
        sender_name_match = re.search(r'^([A-Za-z\s]+)', signature_text, re.MULTILINE)
        if sender_name_match:
            result["sender_name"] = sender_name_match.group(1).strip()
        # 提取签名中的地址行(匹配带邮编的格式)
        address_lines = re.findall(r'^\d+.+?\d{5}$', signature_text, re.MULTILINE)
        if address_lines:
            result["sender_address"] = ', '.join(address_lines)
    
    # 3. 提取医生姓名(支持Dr./Doctor两种前缀)
    doctor_matches = re.findall(r'(?:Dr\.|Doctor)\s+([A-Za-z\s]+?)(?:,|\s|$)', email_text)
    result["doctor_names"] = [name.strip() for name in doctor_matches] if doctor_matches else []
    
    # 4. 地址提取:NLP实体识别+正则补充
    for ent in doc.ents:
        if ent.label_ in ["GPE", "FAC", "ORG"]:
            result["clinic_addresses"].append(ent.text)
    # 正则补充匹配完整街道+城市+邮编格式
    address_matches = re.findall(r'\d+\s+[A-Za-z\s]+,\s+[A-Za-z\s]+,\s*\d{5}', email_text)
    result["clinic_addresses"].extend([addr.strip() for addr in address_matches])
    # 去重处理
    result["clinic_addresses"] = list(set(result["clinic_addresses"]))
    
    # 5. 多日期场景处理:自动识别预约调整并取最终日期
    date_pattern = r'(?:January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{1,2}(?:st|nd|rd|th)?,\s+\d{4}|\d{1,2}(?:st|nd|rd|th)?\s+(?:January|February|March|April|May|June|July|August|September|October|November|December),\s+\d{4}|\d{1,2}/\d{1,2}/\d{4}'
    result["appointment_dates"] = re.findall(date_pattern, email_text)
    
    # 判断是否为预约调整场景,取最后一个日期作为最终日期
    adjust_keywords = ["调整至", "changed to", "rescheduled to", "updated to"]
    if any(keyword.lower() in email_text.lower() for keyword in adjust_keywords):
        if result["appointment_dates"]:
            result["final_appointment_date"] = result["appointment_dates"][-1]
    else:
        if result["appointment_dates"]:
            result["final_appointment_date"] = result["appointment_dates"][0]
    
    # 6. 提取预约时间
    result["appointment_times"] = re.findall(r'\d{1,2}:\d{2}\s*[APap][Mm]', email_text)
    
    # 7. 提取手机号
    result["mobile_numbers"] = re.findall(r'(\d{10}|\(\d{3}\)\s*\d{3}-\d{4})', email_text)
    
    return result

def parse_multiple_emails(mail, search_criteria):
    status, email_ids = mail.search(None, search_criteria)
    email_results = []
    # 遍历所有匹配的邮件ID
    for email_id in email_ids[0].split():
        status, email_data = mail.fetch(email_id, '(RFC822)')
        email_result = parse_single_email(email_data)
        email_results.append({"email_id": email_id, "data": email_result})
    return email_results

# 使用示例
# search_criteria = 'TEXT "Cityville Medical Clinic"'
# all_results = parse_multiple_emails(mail, search_criteria)
# for res in all_results:
#     print(f"邮件ID: {res['email_id']}")
#     print("提取结果:", res['data'])
#     print("---")

关键改进点说明

  • 批量邮件支持:parse_multiple_emails函数遍历所有匹配邮件,生成每封邮件的结构化结果
  • 地址提取增强:结合spaCy实体识别与正则匹配,覆盖更多地址格式
  • 医生姓名扩展:支持Dr.和Doctor两种前缀,兼容更多称呼方式
  • 智能日期处理:通过关键词识别预约调整场景,自动选取最终日期
  • 签名解析优化:匹配多种签名开头格式,精准提取发件人姓名与地址
  • 编码兼容:自动识别邮件字符集,避免内容乱码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 00:15:54