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

如何批量解析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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 02:24:34