基于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
相关产品推荐
相关产品推荐

