如何编写文本去重脚本

wen 实用脚本 30

从零到精通的完整指南

目录导读

  1. 为什么需要文本去重脚本?
  2. 核心去重算法原理解析
  3. Python实战:编写一个基础去重脚本
  4. 进阶:基于相似度的高效去重方案
  5. 常见问题与优化技巧(问答篇)

为什么需要文本去重脚本?

在大数据时代,重复文本无处不在:爬虫采集的新闻、用户评论、论文查重、知识库清理……如果手动处理,不仅效率低下,还容易遗漏,一个高效的文本去重脚本能自动识别并剔除重复项,节省存储空间、提升数据质量。

如何编写文本去重脚本

核心问题:什么是“重复”?

  • 完全重复:两段文本字符完全一致。
  • 近似重复:仅有个别字符差异(如标点、空格、同义词替换),但语义相同。

我们的脚本需要应对这两种情况。


核心去重算法原理解析

哈希法(最快速)

将每一行文本通过哈希函数(如MD5、SHA1)生成唯一指纹,如果指纹相同,则判定为重复。

import hashlib
def md5(text):
    return hashlib.md5(text.encode()).hexdigest()

优点:速度极快。
缺点:无法识别近似重复(比如仅多一个空格的两句话)。

SimHash(适合长文本近似去重)

Google提出的算法:将文本转为64位指纹,通过汉明距离(Hamming Distance)判断相似度,如果两个指纹差异小于等于3位,则视为相似。

MinHash + Jaccard相似度(通用方案)

将句子拆分为Shingle(如2-gram),计算集合的Jaccard系数,当相似度超过阈值(如0.8)时,判定为重复。


Python实战:编写一个基础去重脚本

完整代码示例(支持文件与命令行参数)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import os
from collections import OrderedDict
def remove_duplicates(input_file, output_file):
    seen = set()
    unique_lines = []
    with open(input_file, 'r', encoding='utf-8') as f:
        for line in f:
            line_stripped = line.strip()
            if line_stripped and line_stripped not in seen:
                seen.add(line_stripped)
                unique_lines.append(line_stripped)
    with open(output_file, 'w', encoding='utf-8') as f:
        f.write('\n'.join(unique_lines))
    print(f"去重完成!原始行数:{len(seen)+len(unique_lines)-1},去重后行数:{len(unique_lines)}")
if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("用法:python dedup.py 输入文件.txt 输出文件.txt")
        sys.exit(1)
    remove_duplicates(sys.argv[1], sys.argv[2])

运行方法

python dedup.py input.txt output.txt

局限性:该脚本仅处理精确重复,如果需要处理“近似重复”,请看下一节。


进阶:基于相似度的高效去重方案

1 使用fuzzywuzzy库识别近似重复

from fuzzywuzzy import fuzz
from collections import deque
def fuzzy_dedup(lines, threshold=80):
    unique = []
    for line in lines:
        is_dup = False
        for existing in unique:
            if fuzz.ratio(line, existing) >= threshold:
                is_dup = True
                break
        if not is_dup:
            unique.append(line)
    return unique

性能提示:对于大数据量,逐对比较是O(n²)复杂度,建议先按长度分组,或使用SimHash预过滤。

2 集成SimHash的工业级方案

from simhash import Simhash, SimhashIndex
def build_simhash_index(lines):
    data = [(str(i), Simhash(line)) for i, line in enumerate(lines)]
    index = SimhashIndex(data, k=3)  # k为汉明距离阈值
    return index

该方案能快速定位与给定文本相似的候选,适用于百万级数据。


常见问题与优化技巧(问答篇)

Q1:我的数据量极大(几千万行),内存不够怎么办?

A:采用分片+外排序策略。

  • 将文件按首字母或哈希值分成多个小文件(如每个100万行)。
  • 分别对每个小文件去重。
  • 最后合并结果,再对合并后的文件跨片去重(因为可能跨片重复)。

Q2:如何区分“标题重复”和“内容重复”?

A:在脚本中添加优先级策略

  • 第一遍:按标题哈希去重(保留最完整版本)。
  • 第二遍:对去重后的集合做内容相似度过滤。
    示例代码片段:
    def dedup_by_title(articles):
      seen_titles = set()
      result = []
      for art in articles:
          title_hash = md5(art['title'])
          if title_hash not in seen_titles:
              seen_titles.add(title_hash)
              result.append(art)
      return result

Q3:脚本运行很慢,如何优化?

A:从以下三方面入手。

  1. 使用生成器:避免一次性读入所有数据到内存。
  2. 多进程并行:按行分割后使用multiprocessing分片处理。
  3. 选择合适算法:精确去重用哈希法,近似去重用SimHash(速度是fuzzywuzzy的百倍以上)。

Q4:如何处理中文停用词和符号?

A:在计算哈希或相似度前,先对文本做预处理:

import re
def clean_text(text):
    # 移除标点符号、空格、换行
    text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9]', '', text)
    # 可选:移除停用词(如“的”、“是”、“在”)
    return text

总结与最佳实践

编写文本去重脚本的核心步骤:

  1. 明确需求:精确去重还是近似去重?
  2. 选择算法:小数据用哈希,大数据用SimHash或MinHash。
  3. 考虑扩展性:脚本应支持管道操作、文件格式多样化(如JSON、CSV)。
  4. 性能调优:先做数据清洗(去除空行、特殊字符),再跑去重。

推荐逻辑顺序

  • 第一层:精确哈希去重(移除完全相同的行)。
  • 第二层:长度分组 + 相似度阈值(移除近似重复)。
  • 第三层:人工抽检(针对特殊场景,如法规文档)。

将脚本封装为函数,方便集成到更大规模的数据清洗流水线中,反复测试不同阈值(如SimHash的k值从3到6),找到最适合你数据集的平衡点。


本文参考了Python官方文档、fuzzywuzzy库手册以及SimHash论文的中文解读,结合搜索引擎中常见去重方案的精髓进行重构编写,确保技术细节准确且易于理解。

抱歉,评论功能暂时关闭!