如何批量解析3万+封邮件正文?Pandas处理邮件文本报错排查
问题诊断
你的整体处理思路可行,所有报错的核心原因是数据类型不匹配:
pandas读取CSV时,默认会将空单元格、解析失败的单元格识别为float类型的numpy.nan空值,而email_reply_parser以及所有字符串处理方法(lower()/replace()/正则匹配)都只接受字符串或字节类型输入,碰到NaN就会抛出你贴出的三类错误。
具体出问题的环节:
- Power Query拉取邮件数据时,无正文的空邮件、导出时解析异常的正文,转成CSV后对应单元格为空,pandas默认解析逻辑没有做类型强制,导致正文列混入float类型的空值
- 调用
apply()处理整列前没有做类型校验和空值兜底,函数拿到非字符串值直接崩溃 - 额外注意:
email_reply_parser默认规则对国内企业常用的中英文混合回复分隔符(比如“-----原始邮件-----”“发件人:xxx 发送时间:xxx”)适配较差,直接裸用会出现最新回复切分不全、历史回复内容残留的问题。
落地处理步骤
1. 读取阶段做类型兜底,从根源消灭float类型脏数据
读CSV时直接强制正文列为字符串类型,空值统一填充为空字符串,避免后续所有类型报错:
import pandas as pd import re from email_reply_parser import EmailReplyParser # 读取时指定正文列类型,空单元格不转NaN df = pd.read_csv( "your_mail_export.csv", dtype={"Body.TextBody": str}, keep_default_na=False ) # 如果是已经读入内存的DataFrame,补做这一步清洗即可 df["Body.TextBody"] = df["Body.TextBody"].fillna("").astype(str)
2. 提取邮件最新回复内容
先兼容国内常见的邮件回复分隔符做预切分,再用email_reply_parser做兜底解析,避免默认规则适配差的问题:
def extract_latest_reply(mail_body: str) -> str: # 统一换行符,消除Windows/Mac系统换行差异 mail_body = mail_body.replace("\r\n", "\n").replace("\r", "\n") # 预切分常见中英文回复分隔符 split_patterns = [ r"\n-{3,}\s*原始邮件\s*-{3,}", r"\nFrom: .*?\nSent: ", r"\n发件人:.*?\n发送时间: ", r"\n_{3,}", r"\n在.*?写道:\s*$" ] for pattern in split_patterns: mail_body = re.split(pattern, mail_body, flags=re.IGNORECASE|re.MULTILINE)[0] # 调用库做兜底解析 latest_content = EmailReplyParser.parse_reply(mail_body) # 清理多余空行和首尾空白 latest_content = re.sub(r"\n{3,}", "\n\n", latest_content.strip()) return latest_content # 应用到正文列,此时不会再出现float相关报错 df["cleaned_body"] = df["Body.TextBody"].apply(extract_latest_reply)
3. 批量移除员工姓名
注意先将员工姓名按长度倒序排列,避免短姓名先替换破坏长姓名匹配;如果担心误匹配,可以加中文边界做正则替换:
# 替换成你自己的员工姓名列表,按长度倒排 staff_name_list = ["张三", "李四", "王五五"] staff_name_list = sorted(staff_name_list, key=lambda x: len(x), reverse=True) def remove_name_tags(text: str) -> str: for name in staff_name_list: # 普通替换逻辑,适合确定不会出现姓名嵌套的场景 # text = text.replace(name, "") # 正则边界匹配逻辑,避免误删包含员工姓名的其他词汇 text = re.sub(rf"(?<![\u4e00-\u9fa5]){re.escape(name)}(?![\u4e00-\u9fa5])", "", text) # 清理替换后多余的空白字符 text = re.sub(r"\s+", " ", text).strip() return text df["final_body"] = df["cleaned_body"].apply(remove_name_tags)
补充说明
- 1.5万行的数据量用
apply处理没有性能问题,全量跑完通常在10秒内 - 建议先随机抽20-30封不同格式的邮件做小样本测试,调整分隔符规则,确认切分逻辑符合预期后再跑全量
- 如果正文残留少量HTML标签,可以加一步
re.sub(r"<.*?>", "", text)清理即可
内容的提问来源于stack exchange,提问作者Lehas123
相关产品推荐
相关产品推荐

