ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

杂标题处理实战:从清洗、解析到去重的完整流程

杂标题处理实战:从清洗、解析到去重的完整流程 前一段时间整理一批网络转载内容为了给内容库做标签和归档我把标题批量导出来看了一眼。成片标题里混着中文、英文、日文有的带着方括号标识有的带一串表情符号还有一条写着“【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw”。第一眼看上去这不过是一条让人摸不着头脑的标题。但如果你做的是内容管理系统、爬虫管道或者给社区内容做检索和推荐这样一条标题其实就是最典型的输入样本信息密度低语言不确定符号不标准还夹着网络用语。我后来把处理这类标题的过程拆成了一条流水线发现真正决定后续检索质量的不是模型多聪明而是最前面几层预处理做没做干净。这篇就围绕“标题处理”讲清楚工程上怎么走。这里不会假装所有问题都靠一个模型解决更多是分享我在做文本预处理、去重、归档时的实践经验以及遇到乱码和误判时到底应该先查哪里。1. 先别急着上模型标题清洗才是第一道关卡很多人拿到一堆杂标题第一反应是“分词、向量化、分类”。但实际跑下来会发现模型效果差往往不是因为算法不行而是输入太脏。像“ミリプロ”“ぬふw”“°”这类内容如果不在前期做归一化后面无论是做关键词提取、文本相似度计算还是建索引都会把噪声当成有效信号。标题清洗的核心目标不是把标题“变短”而是把同一类内容在不同表达下的写法拉回到同一个坐标系里。对单一标题来说清洗看起来只是去掉几个符号对批量数据来说清洗决定了后续所有步骤的稳定性。1.1 字符层不是“去空格”那么简单先说最容易踩坑的字符层。一个标题从网页、App 端、后台数据库导出可能经历过多次编码转换。最常见的现象是全角半角混用、日文假名变成乱码、不可见字符残留在字符串中间。我在处理时会先做一次 Unicode 规范化把全角英文、全角数字、全角标点统一成半角再去掉不可见控制字符和多余空白。Python 里常见的写法是这样import re import unicodedata def normalize_title(title: str) - str: # 先把全角字符转成半角同时也处理很多兼容字符 s unicodedata.normalize(NFKC, title) # 控制字符、零宽字符统一移除 s .join(ch for ch in s if not unicodedata.category(ch).startswith(C)) # 多个空格压缩成一个 s re.sub(r\s, , s) return s.strip()注意NFKC 规范并不等于“变干净”。它会把“”变成“%”把全角“”变成“A”这确实有利于后续比对。但它也可能改变某些字符的语义所以清洗后的结果只应该作为后续检索和去重的中间字段原始标题不能丢。很多团队在这里犯的错是“过度清洗”。他们把标题里的表情符号、方括号、颜文字全部删掉最后剩下一串干巴巴的内容。如果这是一条新闻标题可能问题不大如果是社区内容或二次元转载内容这些“噪声”本身就是内容的一部分。比如标题里的“ぬふw”如果目标是做内容去重“ぬふ”和“w”可以忽略如果目标是做社区画风分析这两个字符其实是有信息量的。所以我在清洗时会把规则拆成两层第一层做 Unicode 规范化和不可见字符清理第二层根据下游任务决定是否移除标点、表情、语气词。不要在一开始就一刀切。1.2 方括号不是噪音是结构化线索“【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw”这种标题最显眼的结构是方括号。很多人看到方括号会下意识删掉这会丢掉很重要的分类信息。方括号在内容平台里通常承担着标签、分区、活动前缀、转载来源等作用。正确做法是把“【】”里的内容先解析出来再决定保留还是丢弃。比如“ミリプロ/转载”里可以拆出“ミリプロ”和“转载”两个字段前者是内容主题后者是内容类型。“3字以心传心”可能是一场活动、一个 tag 或一个系列名。解析不必一开始就用模型。先用方括号做粗切分再看每个片段内部有没有分隔符比如斜杠、顿号、空格。常见正则写法def parse_bracket_tags(title: str): # 匹配中文【】和英文[]两种括号 tags re.findall(r[【\[]([^】\]])[】\]], title) result [] for tag in tags: # 按常见分隔符拆成更细字段 parts re.split(r[/|·\s], tag) result.append([p for p in parts if p]) return result这段逻辑不复杂但在真实数据里非常顶用。通过它一条杂乱的标题会被拆成“前缀标签 主体内容 尾部噪声”三段。后续无论是做标签检索还是人工审核都有了一个更清晰的中间结构。这里要提醒一句这类规则解析对格式一致性依赖很高。如果你面对的标题是从多个平台采集来的有的用“【】”有的用“[]”有的用“【」”规则脚本就要多写几个分支。更好一点的做法是先用样本数据统计一遍括号和分隔符的出现频率再写解析器而不是凭感觉写。2. 语言识别和编码问题会直接影响清洗结果标题里出现日文片假名并不算什么稀罕事。中文社区里经常混入日文缩写、英文词汇、表情符号。面对这种标题语言识别不是“判断它是什么语言”那么轻巧而是要解决“这一小段内容应该按什么规则处理”。“ミリプロ”到底该不该被改成中文如果内容库的检索语言是中文保留日文可能造成搜不到如果用户群体本身习惯日文检索强行翻译反而画蛇添足。更安全的做法是保留原始文本同时抽取出语义分类而不是在清洗阶段把多语言并成一个语言。2.1 短文本语言检测为什么不可靠常规语言检测工具在长文本上准确率很高但标题往往只有几个到十几个字。短文本里日文汉字和中文汉字混在一起语言检测器经常左右摇摆。比如“吐槽担当”四个字是中文字但整体标题又带日文助词和拟声词。如果检测器只看局部很容易判断成中文然后把“ぬふw”当成乱码删掉。所以我不建议在标题级直接相信语言检测的置信度。更稳妥的方案是先做字符集判断看有没有平假名、片假名、韩文谚文等特征字符再做常见词库匹配最后把语言识别结果作为“一个特征”而不是“一个结论”。2.2 用术语表和标签白名单兜底对于“ミリプロ”这类领域缩写与其依赖语言检测不如维护一个小型术语表。因为同一个缩写在不同圈子里可能对应完全不同的含义模型很难从短标题里学到这个知识。人工维护术语表虽然听起来“不智能”但在垂直领域里效果远好于通用模型。我在实际落地时会把这样的词表分成三层领域词比如“ミリプロ”“虹深”这类在特定圈子内高频使用的词。停用词比如“转载”“搬运”“吐槽”“担当”这类描述行为或角色的通用词。特殊符号映射比如“w”可以映射为“笑”“°”可以映射为“度”或昵称分隔符。术语表不需要一开始做得很大而是从第一批样本里抽出来整理成 CSV后续每次出现新词再补充。另外如果标题来源是日本内容或者日文输入法打字编码问题也很常见。日文 Shift_JIS 编码的中文系统环境里经常变成乱码。导入数据时最好统一转成 UTF-8并在入库时保存原始编码信息避免二次处理时再猜一次编码。3. 从“无结构标题”中解析出可用的字段清洗和语言识别做完之后标题仍然是一段字符串。如果我们希望后续能够检索“某主题下的转载内容”或者“某个角色的吐槽内容”就需要把这个字符串结构化成字段。这里的字段不一定要很完整但至少要有一个相对稳定的主体关键词和一组标签。3.1 先统计格式再写规则我看到很多人一上来就想套一个通用信息抽取模型效果往往不好。因为标题格式千差万别但同一个来源的标题格式往往高度相似。所以我的经验是先从样本里随机抽出 100 条标题人工看一眼格式分布再决定用什么方法。格式统计可以很简单是否包含方括号方括号里是什么类型内容方括号后面是否还有文字是否包含日文、英文、表情符号是否有统一的分隔符如果 100 条里有 80 条都遵循“【标签/类型】主体内容”那么写规则解析就是性价比最高的方案。只有规则覆盖不了的那部分才考虑模型。解析时要注意“主体内容”和“尾部噪声”的边界。比如“吐槽担当虹深°ぬふw”这个片段“吐槽担当”可能是身份或分工“虹深”是名字或昵称“°”是昵称分隔符“ぬふw”是语气词。如果按逗号、空格、特殊符号切分可能得到“吐槽担当”“虹深”“ぬふw”三块。要不要继续合并取决于下游任务。如果你只需要主题关键词“虹深”就够了。3.2 解析之后保留原始标题做锚点结构化解析不可能 100% 正确尤其面对网络用语和爱称时很容易拆错。所以数据表设计里一定要保留原始标题字段同时把解析结果放在旁边。后续如果发现解析规则有问题可以用原始标题重新跑而不需要重新采集。我在做归档表时通常保留四个字段原始标题、规范化标题、解析标签、清洗中间结果。这样每次人工审核时既能看见“机器怎么理解”也能对照“原始内容到底是什么”。字段设计可以很简单例如raw_title 原始标题 norm_title 规范化后的标题 tags 解析出的标签列表 parse_flag 解析状态0待审核1自动通过2无法解析这个看起来不复杂的表结构实际上会避免很多问题。很多标题解析出错不是规则没写对而是因为没有地方记录“这个过程出了什么错”。有了状态字段你才能知道哪些标题需要人工介入。4. 转载场景里的去重不能只盯着标题做转载内容归档时最头疼的往往是重复内容。同一个内容可能被不同用户改了标题或者在标题前后加了一堆营销词。如果只做完全匹配重复内容会大量漏掉如果做语义相似度又会误杀掉一些本来不同的内容。转载场景里的去重本质上是一个多证据投票问题。标题只是其中一个证据不能单独拍板。4.1 严格去重与近似去重的取舍如果数据量不大先做严格去重是最安全的。把规范化后的标题做哈希相同哈希值直接归到一组。这个方案的优点是零误杀缺点也很明显标题只要多一个空格或标点哈希就完全不一样。如果数据量到了几万条以上可以考虑 SimHash 或 MinHash 这类局部敏感哈希算法。它们能在标题几乎相同但略有差异的情况下把相似文本归为一组。思路不复杂先把文本拆成字符 n-gram映射成向量再做降低维度变成哈希指纹最后比较指纹的海明距离。但这类算法最怕的是“短标题”。标题太短、有效词太少SimHash 的稳定性会变差。一个只有四五个字的标题改一个字就可能从“相似”变成“不相似”。所以在转载内容上我一般不会单独用标题做 SimHash而是先按来源、作者、发布时间做一次粗分组再在组内做文本相似度判断。4.2 低信息量标题要用多重证据补充“【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw”这个标题如果拿去和另一条“【ミリプロ/搬运】虹深吐槽合集”做判断标题相似度可能不高但正文内容很可能高度重合。这种场景下只靠标题去重一定会漏。更合理的做法是把标题、正文段落首句、正文关键词、发布账号这几个维度组合起来。比如标题规范化后计算相似度。抽正文的前 100 字和最后 100 字计算 Jaccard 相似度。如果正文相似度超过阈值再回看标题是否可归一。最终是否判定为重复要留一个人工审核的采样比例。这样做的好处是即使标题被改得面目全非正文特征仍然能提供线索。坏处是处理链路更长因为你需要先抓正文再做正文特征提取。不要指望只靠一个轻量脚本解决转载去重它应该被当作一个持续迭代的流程。5. 把一次经验沉淀成可复用流程单条标题怎么清洗、怎么解析网上有很多代码片段。但真正有价值的是把这些步骤串成一条可重复执行的流水线。这样面对下一批新数据时不用从头开始猜规则。我这里给一个最小可用的流程结构具体参数可以根据你的数据调整。def process_title(raw_title: str) - dict: normalized normalize_title(raw_title) tags parse_bracket_tags(normalized) # 清洗后剩余主体 body remove_tags(normalized) return { raw_title: raw_title, normalized: normalized, tags: tags, body: body, status: done, }这个流程看起来很简单但要注意几个关键点首先每一步都要有日志其次解析不出来的标题要单独落到一个“未解析”目录里第三不要批量处理超大数据之前不做小样本验证。5.1 一条最小可用的标题处理流水线流水线不一定非要上分布式任务。先把单机脚本跑通再考虑并发。落地时可以分四个阶段采集与转码把不同来源的标题统一转成 UTF-8并保留原始编码信息。清洗与结构化做 Unicode 规范化解析括号标签切分主体和尾部噪声。去重与聚类先做精确匹配再做近似匹配最后输出候选重复组。人工复核与反馈把解析失败的样本抽样抽出来由人确认后回填规则和词表。每个阶段都尽量输出一个中间文件而不是只在内存里跑一遍。这样如果后面发现某个环节出错可以快速定位到是清洗、解析还是去重的问题。我在实际项目里一般会在阶段 2 末尾统计“解析成功率”在阶段 3 末尾统计“重复率”。这两个数字不是写在周报里好看的而是用来判断规则是否需要更新。如果解析成功率突然从 90% 掉到 70%大概率是来了新格式的标题需要补规则。5.2 日志和人工复核是流程的一部分很多人的“自动化”只做到脚本能跑但忽略了日志。对于标题处理这种场景日志的价值不在报错而在“这条标题当时是什么状态”。我在日志里至少会记录原始标题、规范化标题、编码转换记录、解析出的标签、是否触发了去重分组、是否进入人工复核队列。这样即便一个月后有人来问“为什么这条标错了”也能翻到当时的处理上下文。人工复核不是效率的反面。相反它是让规则进化的关键。没有人工复核你永远不会知道哪些标题被误判了。每次复核结果都可以变成后续的测试样本验证新规则有没有改坏老场景。建议每次批量跑新数据前先抽 50 到 100 条人工看一遍确认清洗规则和解析规则没有“过拟合”上一批样本。不要直接拿全量数据跑除非你已经很确定规则稳定。6. 遇到乱码、误判和漏判应该按什么顺序排查标题处理里没有“一遍成功”这回事。乱码、标签解析不出来、去重误判几乎是必然出现的。关键是遇到问题时按什么顺序排查才不会越改越乱。我总结的排查顺序是先看现象再看输入再看环境再看参数最后确认是不是工具边界问题。6.1 排查顺序现象、输入、环境、参数、工具边界先说现象。是整条标题乱码还是只有日文部分乱码是正则没匹配到方括号还是匹配到了但拆错了先明确现象能少走很多弯路。其次是输入。检查原始文件是不是被二次编辑过编码是不是已经损坏标题字段里有没有被截断。很多时候问题不在处理代码而在最上游的数据导入。接着看环境。Python 版本、依赖库版本、操作系统文件系统默认编码这些都会影响结果。比如在 Windows 下打开 UTF-8 文件容易因为历史原因出现编码隐患。然后是参数。正则表达式有没有漏掉中文括号拆分时分隔符有没有包含全角斜杠语言检测的置信度阈值是不是设得太高或太低这些参数要单独用小样本验证。最后是工具边界。如果某类标题需要领域知识才能理解规则和小模型都处理不了那就不要硬顶着上模型应该直接把它标记为“需要人工处理”。6.2 常见问题速查表问题现象主要原因排查顺序标题出现乱码原始文件编码和读取编码不一致先确认文件编码再检查导入时是否做了转码日文假名变成了问号字符集不支持检查数据库连接字符集检查导出文件是否为 UTF-8正则匹配不到方括号括号是全角或中文符号检查正则里的字符类是否覆盖【】和[]两种标签解析成空列表方括号被清洗阶段删掉了调整清洗规则保留方括号语言检测判断不准短文本混语种不要依赖单一模型加入术语表和字符集判断去重误判标题太短特征太少加入正文特征、来源、发布时间等证据批量处理速度慢每条都调用重模型先用规则和正则过滤大部分只有少数走模型这张表本身不算什么高深算法但它是排障时的“路线图”。遇到新问题时先往表里对应的行套一套往往能省不少时间。7. 这套方法的适用边界比功能更值得知道聊完流程和排查最后要泼一盆冷水标题处理方案没有银弹。不是什么场景都适合用同一套规则流水线也不是所有标题都值得花大力气结构化。7.1 适合什么场景不适合什么场景这套方法适合内容平台做入库前预处理适合爬虫归档时给文本打标签适合社区内容做检索和推荐前的数据清理也适合个人知识库批量导入时的去重和归一。但它不适合手工处理少量标题因为维护规则和词表的成本超过了手工成本。它也不适合需要深度理解语义的场景比如“判断这条内容是否讽刺”或“判断作者情绪”那是更上层的语言理解问题和标题字符串本身关系不大。即使是在内容处理场景里也要先做好预期管理。规则方法能解决 80% 的格式统一问题剩下 20% 往往需要靠术语表、人工标注和小模型协同。不要指望一次性建好一套永远不变的流程。7.2 长期维护需要补三块术语表、测试集、指标如果你打算把标题处理当成一项长期能力来建设光有脚本不够。我会建议你逐步补上三样东西。第一术语表。不只是领域词还要包含常见错别字、变体写法、表情符号含义。这个词表要持续更新每次人工复核时发现的“老词新写法”都能往里加。第二测试集。从不同来源的标题中抽出一批已经标注好期望结果的样本作为回归测试集。每次修改清洗规则或解析规则后都跑一遍测试集确保旧的正常场景没有被改坏。第三指标。不要只看“处理条数”要看“解析成功率”“去重准确率”“人工复核率”。没有指标就没有持续改进的依据。回到最初那条“【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw”。当它只是屏幕上的一条杂标题时可能觉得无从下手。但把它拆成“标签、主体、语气词、特殊符号”之后你会发现它并没有多特殊。真正决定处理质量的不是用多先进的模型而是你愿意花多少精力维护好最底层的清洗、解析、复核流程。这个经验不只适用于标题大概也适用于所有看起来“脏乱差”的文本数据。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进