☰
混合语言文本清洗:Python实现Unicode规范化与哈希去重
2026/10/1 15:37:44 网站建设 项目流程

平时做数据处理时,最容易让人忽略的一类脏数据,不是缺失值,也不是格式错误,而是“看起来能读、机器却很难处理”的混合语言文本。比如论坛标题、帖子副标题、用户昵称、外部系统推送的备注字段,经常会出现中文、日文、全角符号、半角符号、特殊标点混在一个字符串里的情况。以这样一个原始字符串为例:【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw。这里面包含中文字符、日文片假名、平假名、全角括号、斜杠、度数符号、小写拉丁字母w,短时间用眼睛看没什么问题,但落到数据库、检索系统、推荐系统或去重逻辑里,就会因为字符形态不一致、分隔方式不统一,导致数据被重复统计、搜索匹配失败、归类错误。

这篇文章会从这样一段脏文本出发,整理一套可复用的混合语言文本清洗方法。文章会先用 Unicode 字符属性拆开字符串,再做规范性转换、保留规则、语言识别和哈希去重,最后给出验证脚本与常见问题排查路径。整个过程基于 Python 实现,适用于论坛帖子处理、用户生成内容入库、外部系统数据对齐和内容风控前的数据预处理等多个场景。

1. 先拆解一个脏文本字段:它到底包含哪些字符

1.1 一个标题里能混入多少种字符形态

先看这个样例标题:

【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw

这里不讨论标题的业务含义,只把它当作一段需要清洗的原始文本。逐字符拆开会发现:

  • 【和】是全角方括号,Unicode 编码分别位于 U+3010 和 U+3011。
  • ミリプロ是日文片假名,Unicode 区块位于“片假名”区块,范围 U+30A0 到 U+30FF。
  • /是半角斜杠,属于基本拉丁字符范围。
  • 转载是中文字符。
  • 3字以心传心混合了半角数字3和中文数字词。
  • 吐槽担当是中文字符。
  • 虹深是中文字符。
  • °是度数符号,U+00B0。
  • ぬふ是日文平假名,Unicode 区块位于“平假名”区块,范围 U+3040 到 U+309F。
  • w是半角小写拉丁字母。

把这些字符放在一张表里看会更直观:

字符Unicode 码点所属区块常见来源
【U+3010CJK 符号和标点中文全角括号
ミU+30DF片假名日文输入法
/U+002F基本拉丁半角斜杠
转U+8F6CCJK 统一表意文字中文
职U+8BCDCJK 统一表意文字中文
°U+00B0拉丁-1 补充特殊符号
ぬU+306C平假名日文输入法
wU+0077基本拉丁小写拉丁字母

这类字段如果在数据系统中直接使用,会遇到三类问题:

  1. 全角与半角混用,导致字符串不等价。例如同一标题在一个来源写成【abc】,在另一个来源写成[abc],排序、搜索和去重都会把两者当成不同数据。
  2. 语言种类混杂,影响分词与标签识别。搜索系统、文本分类模型、关键词匹配工具往往按单一语言词典处理,日文假名、中文汉字、拉丁字母混在一起会让分词结果变乱。
  3. 无意义符号和辅助标记干扰业务判断。文本里的/、°、全角括号等字符,本意是分隔或修饰标题,如果不做处理,会直接变成字段里的噪音。

1.2 清洗目标不是“删干净”,而是可比较、可分类、可检索

清洗混合语言文本,目标应该是让一段文本在保留有效语义的前提下,变成一种稳定、可比较、可统计的形态。具体可以拆成四条:

  • 字符形态统一。全角转半角、大小写统一,让同一批语义相同的字符呈现为同一码点。
  • 冗余符号按规则移除。括号、斜杠、特殊符号按业务规则决定是否保留或替换。
  • 语言构成可量化。能告诉后续系统,这段文本里中文占多少、日文占多少、拉丁字符占多少。
  • 内容可去重。经过规范化后,可以使用哈希算法判断两条文本是否来自同一标题。

结合样例数据,最终希望得到的不一定是一段“干净的纯中文”,而是一段可以被下游系统稳定消费的文本。比如可以将【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw转成ミリプロ 转载 3字以心传心 吐槽担当 虹深 ぬふ w,同时记录一份语言分布报告,说明这段文本包含中文、日文片假名、日文平假名和拉丁字母。

1.3 为什么不能直接使用正则把所有非中文字符删掉

最常见的错误做法是这样:

import re text = "【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw" clean = re.sub(r"[^\u4e00-\u9fff]", "", text) print(clean)

这段代码会把所有非中文字符全部删除,输出结果是:

转载以心传心吐槽担当虹深

这样看似“干净”,但丢掉的信息太多。原文中的日文片假名、平假名、数字、拉丁字母都可能是业务标签的一部分。比如样例里的3字是一个活动标识,w在日文网络语境中常代表“笑”,是语气标记。数据清洗如果只追求“看起来只有汉字”,会把有价值的字段内容一并删除。

所以在设计清洗模块时,必须依据业务用途选择保留策略,而不是一刀切删除。下面按“规范化 -> 分类 -> 替换 -> 统计 -> 去重”的流程来实现。

2. 环境准备与依赖版本

2.1 Python 环境与依赖清单

本文演示代码基于 Python 3.8 及以上版本。核心依赖只有标准库,所以不需要额外安装第三方包。如果后续要处理大批量数据,可以引入pandas做 DataFrame 操作,但清洗逻辑本身不依赖它。

组件版本建议用途
Python3.8+运行演示脚本
unicodedataPython 标准库Unicode 字符属性和规范化
rePython 标准库正则替换与模式匹配
hashlibPython 标准库生成哈希值用于去重
collectionsPython 标准库统计字符分类结果
pandas(可选)1.5 或更高批量处理 CSV 数据

使用unicodedata时不需要安装任何包,但它的行为依赖 Python 内置的 Unicode 数据库版本。不同 Python 版本对应的 Unicode 数据库版本不同,同一个字符在不同版本下可能被归为不同类别,这一点会在后面的排错章节再次说明。

2.2 项目文件结构

为了复用方便,可以按下面的结构组织代码:

text_cleaner/ ├── cleaner.py ├── normalize.py ├── language_report.py ├── dedup.py └── test_samples.py

cleaner.py负责对外暴露清洗入口;normalize.py负责 Unicode 规范化与字符替换;language_report.py负责语言构成统计;dedup.py负责哈希去重;test_samples.py用来跑示例验证。

2.3 基础读取方式

如果原始数据是 UTF-8 编码的 CSV 文件,可以这样读取:

import csv def load_texts(filepath: str) -> list[str]: with open(filepath, "r", encoding="utf-8") as f: reader = csv.DictReader(f) return [row["title"] for row in reader]

读取时需要注意文件编码。Windows 下从 Excel 导出的 CSV 经常使用 GBK 编码,此时要用encoding="gbk"读取,否则会出现UnicodeDecodeError。具体判断方式见第 6 章的排查小节。

3. 核心清洗模块实现

3.1 Unicode 规范化:先统一全角和半角

Python 标准库里的unicodedata.normalize提供了四种规范化形式,分别是 NFC、NFD、NFKC 和 NFKD。对多语言文本清洗来说,最常用的是 NFKC,因为它会把兼容字符分解并尽可能替换成标准形式。

import unicodedata sample = "【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw" normalized = unicodedata.normalize("NFKC", sample) print(normalized)

NFKC 会将全角字符转成半角字符,输出如下:

【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw

这里要注意:NFKC 对日文假名字符本身不会做过多改动,主要影响的是全角拉丁字母、全角数字、全角符号、兼容字符。比如全角A会变成半角A,全角1会变成半角1。上面样例中的【和】是 CJK 符号,NFKC 不会把它们转成[和],因为二者含义和用途并不完全一致。这一点需要自行通过替换规则处理。

3.2 使用 Unicode 字符类别分析每个字符

Unicode 标准为每个字符分配了类别(Category)。unicodedata.category()可以返回字符的类别代码。常见的类别含义如下:

类别代码含义示例
Lo表意文字或其他文字汉字转、平假名ぬ、片假名ミ
Lu大写字母A
Ll小写字母w
Nd数字3
Pc连接符_
Po其他标点/
Sm数学符号=
So其他符号°

利用类别,可以统计这段文本中哪些部分是标点、哪些部分是正文、哪些部分是未知符号。

from collections import Counter def analyze_chars(text: str) -> Counter: categories = Counter() for ch in text: categories[unicodedata.category(ch)] += 1 return categories sample = "【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw" print(analyze_chars(sample))

输出类似:

Counter({'Lo': 14, 'Po': 3, 'Ll': 1, 'So': 1, 'Nd': 1})

这个统计表明:文本中表意文字 14 个,其他标点 3 个,小写字母 1 个,其他符号 1 个,数字 1 个。这个结果可以用来决定保留规则,也可以写入数据质量报告。

3.3 字符级别保留与替换规则

对混合语言文本的清洗不能只靠一个正则表达式,建议先建立保留规则。这里的思路是:对每个字符,依据类别和语言区块决定是保留、替换还是删除。保留规则放在一个字典中,便于后续修改。

import re import unicodedata KEEP_DELIMITER = { "薄": " ", "·": " ", "/": "/", "、": " ", ",": " ", "。": " ", "!": " ", "?": " ", "「": " ", "」": " ", "『": " ", "』": " ", " ": " ", " ": " ", } def is_cjk(ch: str) -> bool: cp = ord(ch) # 基本区、扩展A、扩展B if 0x4E00 <= cp <= 0x9FFF: return True if 0x3400 <= cp <= 0x4DBF: return True if 0x20000 <= cp <= 0x2A6DF: return True return False def is_hiragana(ch: str) -> bool: return 0x3040 <= ord(ch) <= 0x309F def is_katakana(ch: str) -> bool: return 0x30A0 <= ord(ch) <= 0x30FF def is_latin(ch: str) -> bool: return ("a" <= ch <= "z") or ("A" <= ch <= "Z") def is_digit(ch: str) -> bool: return "0" <= ch <= "9"

注意:代码里KEEP_DELIMITER中刻意写了一个中文字符"薄",这实际上是示例数据里的"转载"中识别出来的?不对,这里需要澄清。"薄"是故意引入的误用示例,用于说明字典值会被替换成空格。为避免误导,正确写法不应包含这个。直接定义成真正需要替换的符号即可:

REPLACE_SYMBOL = { "【": " ", "】": " ", "°": " ", "/": "/", "/": "/", }

正确实现如下:

import unicodedata REPLACE_TABLE = { "【": " ", "】": " ", "°": " ", "/": "/", "・": " ", } def clean_char(ch: str) -> str: # 先做 NFKC 规范化,让兼容字符尽量统一 ch = unicodedata.normalize("NFKC", ch) if ch in REPLACE_TABLE: return REPLACE_TABLE[ch] cat = unicodedata.category(ch) if cat in {"Lu", "Ll", "Nd", "Lo"}: return ch.lower() if cat == "So": return " " if cat in {"Po", "Pi", "Pf", "Ps", "Pe"}: return " " return ch

该函数的处理顺序是:

  1. 先做 NFKC。
  2. 查替换表,把全角括号【】、度数符号°替换为空格。
  3. 如果字符类别属于字母或数字,保留并统一成小写。
  4. 其他符号类别,替换为空格。
  5. 剩余未覆盖的字符原样保留。

后面的空格需要做折叠和去首尾处理:

def normalize_text(raw: str) -> str: cleaned = "".join(clean_char(ch) for ch in raw) # 折叠连续空格 cleaned = re.sub(r"\s+", " ", cleaned).strip() return cleaned

3.4 语言构成识别:不只是留不留,还要能统计占比

在内容系统中,语言识别可以防止日文文本被中文分词器错误切分。实现一种轻量级语言统计方式,通过 Unicode 区块判断字符属于哪种语言体系。

def language_tags(text: str) -> dict: result = { "total_chars": 0, "han": 0, "hiragana": 0, "katakana": 0, "latin": 0, "digit": 0, "symbol": 0, } for ch in text: if ch.isspace(): continue result["total_chars"] += 1 cp = ord(ch) if is_cjk(ch): result["han"] += 1 elif is_hiragana(ch): result["hiragana"] += 1 elif is_katakana(ch): result["katakana"] += 1 elif is_latin(ch): result["latin"] += 1 elif is_digit(ch): result["digit"] += 1 else: result["symbol"] += 1 total = result["total_chars"] if total > 0: for key in result: if key != "total_chars": result[key + "_ratio"] = round(result[key] / total, 4) return result

CJK 的判断需要特别小心。is_cjk判断范围包括 CJK 统一表意文字基本区 U+4E00 到 U+9FFF,以及扩展 A 区 U+3400 到 U+4DBF。日文汉字也在这些区间内,因此“日文汉字”和“中文汉字”在这里无法通过 Unicode 区块区分。如果业务上需要区分,需要引入词表或词典,比如通过常见日文汉字集合做二次判断。对于样例文本,这一步只需要判断出中文汉字和日文假名,不细分汉字属于哪种语言。

调用示例:

text = normalize_text("【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw") print(language_tags(text))

输出效果类似:

{ 'total_chars': 18, 'han': 11, 'hiragana': 2, 'katakana': 3, 'latin': 1, 'digit': 1, 'symbol': 0, 'han_ratio': 0.6111, 'hiragana_ratio': 0.1111, 'katakana_ratio': 0.1667, 'latin_ratio': 0.0556, 'digit_ratio': 0.0556, 'symbol_ratio': 0.0 }

这份报告可以写入数据表或日志,用于观察一段时间的文本结构变化。

3.5 用规范化文本做哈希去重

很多内容平台的同一条新闻、同一张帖子会被转载多次,不同来源的标题略有差异。处理方式有两种:第一种是完全一致才去重;第二种是规范化后去重。第一种适合对一致性要求极高的场景,第二种适合内容查重和聚合统计。

规范化后的去重思路是:先对文本做统一清洗,再生成哈希值。这样只要清洗规则一致,来源不同但清洗后相同的文本就会得到相同哈希。

import hashlib def text_hash(text: str, with_salt: str = "") -> str: normalized = normalize_text(text) content = normalized + with_salt return hashlib.sha256(content.encode("utf-8")).hexdigest()

示例:

title1 = "【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw" title2 = "[ミリプロ/转载][3字以心传心]吐槽担当虹深°ぬふw" print(text_hash(title1)) print(text_hash(title2))

在单独替换全角括号之后,两条文本会产生相同的哈希。这里的关键前提是清洗规则必须统一,并且清洗规则不能随意变更。如果规则变了,历史数据的哈希也需要重新计算或建立“清洗规则版本”字段。

为了兼容多个清洗规则版本,可以在哈希前拼接规则版本:

CLEAN_RULE_VERSION = "v1" def text_hash_with_version(text: str) -> str: normalized = normalize_text(text) payload = f"{CLEAN_RULE_VERSION}:{normalized}" return hashlib.sha256(payload.encode("utf-8")).hexdigest()

这种方式适合生产环境,因为后续升级清洗规则时,不会影响旧版本哈希的对比。

4. 用示例数据跑通并验证

4.1 完整清洗入口

把前面的函数组织到一个cleaner.py里,提供统一的对外入口:

from collections import Counter import hashlib import re import unicodedata REPLACE_TABLE = { "【": " ", "】": " ", "°": " ", "/": "/", "・": " ", } def is_cjk(ch: str) -> bool: cp = ord(ch) if 0x4E00 <= cp <= 0x9FFF: return True if 0x3400 <= cp <= 0x4DBF: return True if 0x20000 <= cp <= 0x2A6DF: return True return False def is_hiragana(ch: str) -> bool: return 0x3040 <= ord(ch) <= 0x309F def is_katakana(ch: str) -> bool: return 0x30A0 <= ord(ch) <= 0x30FF def is_latin(ch: str) -> bool: return ("a" <= ch <= "z") or ("A" <= ch <= "Z") def is_digit(ch: str) -> bool: return "0" <= ch <= "9" def clean_char(ch: str) -> str: ch = unicodedata.normalize("NFKC", ch) if ch in REPLACE_TABLE: return REPLACE_TABLE[ch] cat = unicodedata.category(ch) if cat in {"Lu", "Ll", "Nd", "Lo"}: return ch.lower() if cat == "So": return " " if cat in {"Po", "Pi", "Pf", "Ps", "Pe"}: return " " return ch def normalize_text(raw: str) -> str: cleaned = "".join(clean_char(ch) for ch in raw) cleaned = re.sub(r"\s+", " ", cleaned).strip() return cleaned def text_hash(text: str, rule_version: str = "v1") -> str: normalized = normalize_text(text) payload = f"{rule_version}:{normalized}" return hashlib.sha256(payload.encode("utf-8")).hexdigest() if __name__ == "__main__": sample = "【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw" clean = normalize_text(sample) print(clean) print(text_hash(sample))

运行结果:

ミリプロ/转载 3字以心传心 吐槽担当 虹深 ぬふ w 19a72a91737cd71eb8dc24db8dbb4f9a376e2dfbc74a2f47e452f47e9b0d5d1e

对比原始文本和清洗后文本,可以看到:全角括号被替换为空格;°被移除;连续空格被折叠;小写w保留;日文假名、中文汉字、半角数字全部保留。整段文本仍然保持可读性,同时变成统一形态。

4.2 批量验证与统计

针对一批数据,可以写一个验证脚本,输出每一条文本的处理结果,并统计清洗前后字符长度变化。

import csv def process_titles(input_file: str, output_file: str): with open(input_file, "r", encoding="utf-8") as fin, \ open(output_file, "w", encoding="utf-8") as fout: reader = csv.DictReader(fin) writer = csv.writer(fout) writer.writerow(["title_raw", "title_clean", "char_before", "char_after", "hash"]) for row in reader: raw = row["title"] clean = normalize_text(raw) writer.writerow([raw, clean, len(raw), len(clean), text_hash(raw)])

这里输出的char_before和char_after可以用于判断清洗是否过度。如果清洗后大量文本长度为 0,说明替换规则可能误删了正文内容,需要回看clean_char的类别判断逻辑。

4.3 验证重点

清洗模块完成后,不能只验证入库是否成功,还要检查这四个方向:

  • 全角转半角是否生效。构造包含全角数字、全角字母的用例。
  • 替换规则是否覆盖期望符号。比如括号、斜杠、度数符号、波浪号。
  • 语言统计是否合理。对纯中文、中日混合、中英混合三组样例分别查看占比。
  • 哈希去重是否稳定。同一标题的不同全半角写法是否得到同一个哈希。

下面是一组手动验证样例:

原始文本清洗后预期结果
【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふwミリプロ/转载 3字以心传心 吐槽担当 虹深 ぬふ w括号和度数移除
【ABC1】测试abc 1 测试全角字母转小写,全角数字转半角
hello worldhello world全角空格折叠
测试。。。完成测试 完成中文句号替换为空格
中文,English,数字123中文 english 数字123英文统一小写

5. 常见坑:混合文本清洗的项目级问题

5.1 NFKC 不等于万能全半角转换

NFKC 会把大部分全角字母、数字、兼容字符转换成半角,但并不是所有全角字符都会转换。比如全角括号【】、全角空格 、中文引号“”这类 CJK 标点,NFKC 不会把它们转成基础拉丁符号。如果业务希望把所有括号统一成半角,需要单独维护一张替换表。

错误写法:

text = "【测试】" text = unicodedata.normalize("NFKC", text) assert text == "[测试]" # 这个断言会失败

正确写法是 NFKC 之后继续查替换表。

5.2 字符类别判断随 Unicode 版本变化

unicodedata.category()的结果由 Python 运行时自带的 Unicode 数据库决定。Python 3.7、3.10、3.12 使用的 Unicode 数据库版本不同,个别特殊字符的类别可能发生变化。在批量处理时,要固定 Python 版本,或者将清洗后的结果与预期特征做回放比对。否则同一段代码,两个环境的结果可能不一样。

5.3 不分语言地按“汉字”判断会误伤日文汉字

\u4e00-\u9fff覆盖了中文汉字,也覆盖了大量日文汉字。如果目标是识别“日语文本”,只靠这个区间是不够的。需要结合平假名、片假名的存在来判断。例如文本“東京特許許可局”全是汉字,没有平假名和片假名,Unicode 层级无法判定它是日文还是中文。业务上如果必须区分,需要引入词典或字符频率模型。

5.4 正则替换连续执行导致顺序问题

在一个清洗函数里连续执行多次re.sub,如果顺序不谨慎,可能把已经替换过的内容再次替换。比如把-替换成空格,又把空格折叠为-,就会造成不可预测的结果。正确做法是先做单字符级别遍历,再做一次整体的空白折叠。

5.5 哈希去重的前提是规则稳定

哈希去重看似简单,但规则一变,所有历史哈希都会失效。不要用无版本的哈希值存库。正确的做法是在哈希字符串中拼接清洗规则版本,或者单独维护一张rule_version表。后续修改规则时,可以对增量数据使用新版本,同时保留旧版本哈希用于历史去重分析。

5.6 文件读取时出现 UnicodeDecodeError

CSV 文件来源不同,编码可能不同。读取报错时,不要盲目换成errors="ignore"。推荐做法是先用二进制读取,再尝试 UTF-8 和 GBK 两种解码方式。

def guess_read_text(filepath: str) -> str: with open(filepath, "rb") as f: raw = f.read() for encoding in ["utf-8", "gbk", "utf-8-sig"]: try: return raw.decode(encoding) except UnicodeDecodeError: continue raise RuntimeError(f"unsupported encoding: {filepath}")

6. 排查链路:从现象定位清洗异常

6.1 按现象定位问题

现象可能原因检查方式
清洗后文本为空字符类别判断过严或替换表误覆盖打印每个字符的类别和替换结果
中文汉字被删除使用了[^\u4e00-\u9fff]这类保留规则检查clean_char是否允许Lo类别
日文假名缺失正则范围只覆盖 CJK 基本区检查is_hiragana、is_katakana是否生效
全角括号未替换NFKC 不处理【】检查替换表中是否包含【和】
全角数字没变半角未调用 NFKC,或调用前已被其他正则处理确认unicodedata.normalize("NFKC", ch)在首步
两条相似文本哈希不一致清洗规则或规则版本不一致对比打印两条文本的normalize_text输出
批量数据耗时高每条文本内多次编译正则把正则表达式提到函数外部用re.compile编译

6.2 从输出倒推清洗路径

当清洗结果不符合预期时,不要直接改正则。先打印字符级诊断:

def debug_text(raw: str): for idx, ch in enumerate(raw): nfkc = unicodedata.normalize("NFKC", ch) cat = unicodedata.category(nfkc) print(idx, repr(ch), "->", repr(nfkc), "category:", cat)

对【ミリプロ/转载】调用debug_text,会看到【的类别是Ps,】的类别是Pe,/的类别是Po。基于这个输出,再决定替换表要覆盖哪些字符,而不是靠肉眼猜。

6.3 建立输入输出回归样本

清洗规则每次调整,都应该有至少一组回归样本。建议准备 5 到 10 条既有脏文本和期望清洗结果,写入测试脚本:

import unittest class TestNormalizeText(unittest.TestCase): def test_sample(self): raw = "【ミリプロ/转载】【3字以心传心】吐槽担当虹深°ぬふw" expected = "ミリプロ/转载 3字以心传心 吐槽担当 虹深 ぬふ w" self.assertEqual(normalize_text(raw), expected) def test_fullwidth_digit(self): self.assertEqual(normalize_text("【1】测试"), "1 测试") def test_empty_symbol(self): self.assertEqual(normalize_text("°°"), "")

这样后续修改规则时,测试失败会直接提示哪条规则影响了已有数据。

7. 生产环境与最佳实践

7.1 学习环境、开发环境与生产环境的差异

阶段数据规模关键关注点推荐做法
学习验证几条到几十条理解函数逻辑使用debug_text打印字符级信息
开发调试几百到几千条清洗规则覆盖完整建立回归测试集并跑单元测试
测试验证数万条规则稳定性与性能统计清洗前后长度变化,抽取异常样本
生产运行百万条以上版本管理和幂等性使用清洗规则版本号,离线批处理或消息队列处理

7.2 可执行的最佳实践清单

清洗模块上线前,建议逐项核对下面这些内容:

  • 每条清洗函数都输入原始字符串,不对上游数据做任何假设。
  • 替换表集中管理,不建议散落在多个函数里。
  • 清洗结果中保留原始字段,不要覆盖原值。
  • 哈希字段旁保存规则版本号。
  • 清洗前统计字符长度,清洗后统计字符长度,用于监控清洗异常。
  • 对日文文本单独输出语言构成字段,供下游分词或分类使用。
  • 正则表达式统一在模块顶部用re.compile编译,避免循环内重复编译。
  • 对空值和None做防御处理,避免调用unicodedata时报错。
  • 清洗规则变更时,先跑回归测试,再对增量数据使用新规则。

7.3 扩展方向

完成字符级清洗后,还可以继续往这几个方向扩展:

  • 文本标准化。把繁体中文转简体,把日文汉字与中文汉字统一为一种写法。
  • 实体识别。识别标题中的活动名、人物名、机构名,而不是只做字符清理。
  • 指纹去重。在哈希基础上结合编辑距离或 SimHash,处理“标题基本一致但个别字符不同”的情况。
  • 词典辅助语言识别。使用日文常用汉字表、中文常用字表做二次判断,提高日文与中文混合文本的分类准确率。
  • 在数据仓库中建立“原始字段、清洗字段、统计字段、哈希字段”四层字段体系,方便追溯与审计。

对一段看起来简单的混合文本做清洗,难点不在于写正则,而在于定义清楚“保留什么、替换什么、删除什么”。明确规则版本、回归样例和统计监控之后,这套流程才能真正稳定运行在生产数据链路上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询