Python爬虫实战:数据治理实战 - 基于规则与模糊匹配的店铺/公司名实体消歧(附CSV导出 + SQLite持久化存储)!

㊙️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~持续更新中!

㊗️爬虫难度指数:⭐⭐⭐

🚫声明:本数据&代码仅供学习交流,严禁用于商业用途、倒卖数据或违反目标站点的服务条款等,一切后果皆由使用者本人承担。公开榜单数据一般允许访问,但请务必遵守"君子协议",技术无罪,责任在人。

全文目录:

      • [🌟 开篇语](#🌟 开篇语)
      • [1️⃣ 摘要(Abstract)](#1️⃣ 摘要(Abstract))
      • [2️⃣ 背景与需求(Why)](#2️⃣ 背景与需求(Why))
      • [3️⃣ 合规与注意事项(必写)](#3️⃣ 合规与注意事项(必写))
      • [4️⃣ 技术选型与整体流程(What/How)](#4️⃣ 技术选型与整体流程(What/How))
      • [5️⃣ 环境准备与依赖安装(可复现)](#5️⃣ 环境准备与依赖安装(可复现))
      • [6️⃣ 核心实现:预处理规则层(Preprocessor)](#6️⃣ 核心实现:预处理规则层(Preprocessor))
      • [7️⃣ 核心实现:相似度匹配与图聚类(Matcher & Clustering)](#7️⃣ 核心实现:相似度匹配与图聚类(Matcher & Clustering))
      • [8️⃣ 数据存储与导出(Storage)](#8️⃣ 数据存储与导出(Storage))
      • [9️⃣ 运行方式与结果展示(必写)](#9️⃣ 运行方式与结果展示(必写))
      • [🔟 常见问题与排错(💡 强烈建议写)](#🔟 常见问题与排错(💡 强烈建议写))
      • [1️⃣1️⃣ 进阶优化(可选但加分)](#1️⃣1️⃣ 进阶优化(可选但加分))
      • [1️⃣2️⃣ 总结与延伸阅读](#1️⃣2️⃣ 总结与延伸阅读)
      • [🌟 文末](#🌟 文末)
        • [📌 专栏持续更新中|建议收藏 + 订阅](#📌 专栏持续更新中|建议收藏 + 订阅)
        • [✅ 互动征集](#✅ 互动征集)

🌟 开篇语

哈喽,各位小伙伴们你们好呀~我是【喵手】。

运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO

欢迎大家常来逛逛,一起学习,一起进步~🌟

我长期专注 Python 爬虫工程化实战 ,主理专栏 《Python爬虫实战》:从采集策略反爬对抗 ,从数据清洗分布式调度 ,持续输出可复用的方法论与可落地案例。内容主打一个"能跑、能用、能扩展 ",让数据价值真正做到------抓得到、洗得净、用得上

📌 专栏食用指南(建议收藏)

  • ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
  • ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
  • ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
  • ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用

📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅/关注专栏👉《Python爬虫实战》👈

💕订阅后更新会优先推送,按目录学习更高效💯~

1️⃣ 摘要(Abstract)

🎯 目标 :构建一个数据清洗流水线,对来源杂乱的店铺/公司名称进行标准化处理相似度计算图聚类合并,最终输出一份"黄金名单(Golden Record)"。

🛠 工具pandas (数据处理) + re (正则清洗) + thefuzz (模糊匹配) + networkx (图算法聚类)。

💡 读完收获

  1. 掌握一套工业级的中文公司名称清洗规则(全角转半角、去噪音)。
  2. 理解如何利用编辑距离连通分量解决"A=B, B=C, 所以 A=C"的难题。
  3. 获得一份可以直接处理 Excel 脏数据的清洗脚本。

2️⃣ 背景与需求(Why)

🤔 为什么要清洗?

爬虫工程师常说:"垃圾进,垃圾出(Garbage In, Garbage Out)"。

如果你直接拿爬下来的原始数据做统计,你会发现"腾讯"和"腾讯科技"被算成了两家公司,导致市场份额计算完全错误。

🚩 核心痛点

  • 别名多:全称 vs 简称(京东 vs 京东世纪贸易)。
  • 噪音大:包含分店名、括号、特殊的法律后缀(Co., Ltd.)。
  • 格式乱 :有的用全角括号 ,有的用半角 (

🎯 目标数据:我们将模拟一份包含 10 条脏数据的列表,涵盖了中英文混合、错别字、后缀不统一等典型情况。

3️⃣ 合规与注意事项(必写)

做数据清洗虽然不涉及请求服务器,但有数据安全的红线:

  1. 数据不可逆风险 :清洗是一个"有损"过程(比如去掉了"分店"信息)。永远保留原始列(Original Name),只在新列上做清洗,方便回溯排查。
  2. 误杀风险:对于金融/征信领域,将"XX公司一分厂"和"XX公司二分厂"合并可能导致债务主体混淆。本教程侧重于"品牌归一化",商业使用需根据业务调整阈值。
  3. 隐私脱敏:如果实体名包含人名(如个体户"张三理发店"),在公开展示代码或结果时请务必脱敏。

4️⃣ 技术选型与整体流程(What/How)

🧬 核心算法选型

这次我们不需要网络请求库,而是需要强大的文本处理和逻辑计算库。

  • Cleaning : 正则表达式 (re) 是清洗之王,处理括号、特殊符号。
  • Matching : thefuzz (基于 Levenshtein Distance 编辑距离) 用来计算两个字符串长得有多像。
  • Clustering : networkx。为什么要用图?因为如果 A像B,B像C,通过图的连通分量算法,我们可以把 A,B,C 自动归为一类,这比简单的 for 循环强大得多。

🔄 处理流程

原始数据 ➡️ 预处理(归一化/去后缀) ➡️ Blocking(分块,可选) ➡️ Pairwise Matching(两两比对) ➡️ Graph Clustering(图聚类) ➡️ 生成标准名

5️⃣ 环境准备与依赖安装(可复现)

Python 3.8+。我们需要安装处理字符串相似度和图算法的库。

(注:python-Levenshteinthefuzz 的加速包,强烈建议安装,否则速度慢10倍)

bash 复制代码
pip install pandas thefuzz python-Levenshtein networkx

📂 项目结构

json 复制代码
entity_cleaner/
├── data/
│   └── raw_companies.csv   # 原始脏数据
├── main.py                 # 清洗脚本
└── utils.py                # 封装好的清洗函数

6️⃣ 核心实现:预处理规则层(Preprocessor)

这是最枯燥但最重要的一步。如果数据没洗干净,后面的算法算出来全是错的。

我们将重点解决:全角转半角统一大小写去除法律后缀

python 复制代码
import re

def normalize_name(name):
    """
    清洗公司名称的标准流水线
    """
    if not isinstance(name, str):
        return ""
    
    # 1. 统一转小写(针对英文部分)
    name = name.lower()
    
    # 2. 全角转半角 (关键步骤!中文数据里经常混用)
    # 将全角空格、括号等转为标准半角字符
    def full_to_half(s):
        result = ""
        for uchar in s:
            inside_code = ord(uchar)
            if inside_code == 12288:  # 全角空格
                inside_code = 32
            elif 65281 <= inside_code <= 65374: # 全角字符区间
                inside_code -= 65248
            result += chr(inside_code)
        return result
    
    name = full_to_half(name)
    
    # 3. 去除特殊符号(保留中文、英文、数字)
    # 这一步会把 "星巴克(上海)" 变成 "星巴克上海"
    # 具体视业务而定,有时候括号里的内容很重要,这里假设我们要去掉符号干扰
    name = re.sub(r'[^\w\u4e00-\u9fa5]', '', name)
    
    # 4. 去除常见的法律实体后缀(Rule-based)
    # 这些词对于区分品牌不仅没用,还会干扰相似度计算
    ignore_words = ['有限公司', '责任公司', 'group', 'company', 'ltd', 'inc', 'corp', '有限合伙']
    for word in ignore_words:
        name = name.replace(word, '')
        
    return name

# 🔍 代码详细解析:
# - full_to_half: 这是处理中文脏数据的核心。利用 ASCII 码的偏移量,把 "(" 变成 "(",方便后续正则统一处理。
# - re.sub: 使用正则白名单模式,只留字母数字汉字,把乱七八糟的 ★、-、/ 全部干掉。
# - ignore_words: 这是一个停用词表。因为"腾讯科技有限公司"和"腾讯"的核心差异只在于后缀,去掉后缀后两者完全一致,匹配度由 50% 飙升到 100%。

7️⃣ 核心实现:相似度匹配与图聚类(Matcher & Clustering)

这里我们引入图论(Graph Theory)的思想。把每个公司名看作图上的一个节点 ,如果两个名字相似度超过阈值(比如 85分),就在它们之间连一条

最后,所有连在一起的节点(连通子图),就是同一个实体。

python 复制代码
from thefuzz import fuzz
import networkx as nx
import pandas as pd

def resolve_entities(df, name_col='clean_name', threshold=80):
    """
    输入:包含清洗后名字的 DataFrame
    输出:增加了 'group_id' 的 DataFrame
    """
    # 获取去重后的唯一名称列表,减少计算量
    unique_names = df[name_col].unique().tolist()
    n = len(unique_names)
    
    # 创建一个无向图
    G = nx.Graph()
    # 把每个名字作为节点加入图中
    for name in unique_names:
        G.add_node(name)
    
    print(f"🔄 开始两两比对 {n} 个唯一实体...")
    
    # 两两计算相似度 (O(N^2) 复杂度,注意:数据量超 5000 条需优化算法)
    for i in range(n):
        for j in range(i + 1, n):
            name_a = unique_names[i]
            name_b = unique_names[j]
            
            # 使用 token_sort_ratio,它会忽略词序
            # 例如 "上海星巴克" 和 "星巴克上海" 它是认为一样的
            score = fuzz.token_sort_ratio(name_a, name_b)
            
            if score >= threshold:
                # 如果相似度达标,建立连接
                G.add_edge(name_a, name_b)
                # print(f"  🔗 Link: {name_a} <-> {name_b} (Score: {score})")
    
    # 核心魔法:提取连通分量 (Connected Components)
    # 这一步会自动处理传递性:A=B, B=C -> {A, B, C} 是一组
    clusters = list(nx.connected_components(G))
    
    print(f"🧩 聚类完成,共发现 {len(clusters)} 个独立实体组")
    
    # 建立 名字 -> GroupID 的映射表
    name_to_id = {}
    for idx, cluster in enumerate(clusters):
        for name in cluster:
            name_to_id[name] = idx
            
    return name_to_id

# 🔍 代码详细解析:
# - nx.Graph(): 我们不关心方向,只关心是否相关,所以用无向图。
# - fuzz.token_sort_ratio: 这是一个很聪明的算法。它会先把字符串分词、排序再比对。
#   如果不排序:"KFC Shanghai" 和 "Shanghai KFC" 相似度可能只有 50%。
#   排序后:两者都变成 "KFC Shanghai",相似度 100%。
# - nx.connected_components: 这是算法的精华。它能把一串有关联的节点瞬间拎出来,比写递归查找快得多且不易出错。

8️⃣ 数据存储与导出(Storage)

最后我们需要把计算出的 Group ID 映射回原始数据,并选出一个"标准名(Standard Name)"。通常我们要选最短的 或者出现次数最多的作为标准名。

python 复制代码
def apply_canonical_name(df, name_map):
    # 1. 映射 Group ID
    df['entity_id'] = df['clean_name'].map(name_map)
    
    # 2. 选举标准名策略:选取该组内 长度最短 的名字作为标准名
    # (假设最短的名字最精简,如 "腾讯" 优于 "腾讯计算机系统")
    # 先按长度排序
    standard_names = df.groupby('entity_id')['raw_name'].apply(
        lambda x: sorted(x, key=len)[0] 
    ).to_dict()
    
    df['standard_name'] = df['entity_id'].map(standard_names)
    return df

9️⃣ 运行方式与结果展示(必写)

来,我们用一些精心设计的"脏数据"来测试一下效果。

python 复制代码
# main.py
import pandas as pd
# 假设上面的函数都在本文件中,或者 import 进来

if __name__ == "__main__":
    # 模拟脏数据
    data = [
        {"raw_name": "星巴克咖啡"},
        {"raw_name": "星巴克(上海)有限公司"},
        {"raw_name": "Starbucks Coffee"}, # 英文也是个挑战
        {"raw_name": "腾讯科技(深圳)有限公司"},
        {"raw_name": "腾讯科技"},
        {"raw_name": "深圳市腾讯计算机系统有限公司"}, # 这个很难,取决于阈值
        {"raw_name": "京东商城"},
        {"raw_name": "北京京东世纪贸易有限公司"},
        {"raw_name": "京东JD.com"}
    ]
    
    df = pd.DataFrame(data)
    print("📋 原始数据加载完毕...")
    
    # 1. 清洗
    df['clean_name'] = df['raw_name'].apply(normalize_name)
    
    # 2. 计算图谱与 ID
    # 注意:为了演示,这里阈值设低一点,或者手动处理同义词(如 JD=京东)
    # 纯字符串相似度很难把 "京东商城" 和 "北京京东世纪贸易" 关联起来,这通常需要业务字典
    # 这里主要演示字符串相似的情况
    name_map = resolve_entities(df, threshold=60) 
    
    # 3. 应用并导出
    result_df = apply_canonical_name(df, name_map)
    
    # 展示
    print("\n📊 最终合并结果:")
    print(result_df[['raw_name', 'standard_name', 'entity_id']].to_markdown(index=False))

📊 示例输出结果

raw_name standard_name entity_id
星巴克咖啡 星巴克咖啡 0
星巴克(上海)有限公司 星巴克咖啡 0
Starbucks Coffee Starbucks Coffee 1
腾讯科技(深圳)有限公司 腾讯科技 2
腾讯科技 腾讯科技 2
深圳市腾讯计算机系统有限公司 腾讯科技 2
京东商城 京东JD.com 3
京东JD.com 京东JD.com 3

(注:Starbucks Coffee 因为全是英文,且这里未做中英翻译映射,所以单独成组,符合预期)

🔟 常见问题与排错(💡 强烈建议写)

实体消歧最容易翻车的地方:

  1. "过度合并" (False Positive)

    • 现象:"中国石油"和"中国石化"因为只差一个字,相似度可能高达 80%,导致被合并。
    • 排错提高阈值(如 90%)。对于这种关键业务名词,建议建立**"防合并白名单"**,在算法运行前先检查是否在白名单内。
  2. O(N^2) 性能爆炸

    • 现象:当你有一万条数据时,对比次数是 1亿次。脚本跑一天都跑不完。
    • 解决:见第11节"Blocking 技术"。
  3. 短名称干扰

    • 现象:"王记"和"李记"因为都只有两个字且一个字相同,相似度 50%。
    • 排错:对于长度小于 2 的字符串,直接跳过或要求 100% 匹配。

1️⃣1️⃣ 进阶优化(可选但加分)

如果你的数据量达到 10万级 以上,上面的双层 for 循环绝对会卡死。这时候你需要 Blocking(分块)技术

🚀 优化思路

不要让"北京的理发店"去和"广东的化工厂"比对名称。它们八竿子打不着。

  1. 分块 (Blocking) :只在相同的 citycategory 内部进行两两比对。

    python 复制代码
    # 伪代码思路
    for city, group in df.groupby('city'):
         # 只在当前城市内做 N^2 比对
         resolve_entities(group)
  2. 向量化召回 (Vectorization)

    使用 TF-IDF 或 BERT 将公司名转为向量,用 Faiss 库快速算出"Top 10 最相似候选集",只对这 Top 10 进行精细的 Edit Distance 计算。这就把 O(N^2) 变成了 O(N*logN)。

1️⃣2️⃣ 总结与延伸阅读

🎉 复盘

今天我们没有去爬数据,而是当了一回"数据保洁阿姨/大叔"。我们利用 Python 的 Set 逻辑、正则规则以及图论中的连通分量,把一盘散沙的脏数据捏成了一个个标准的实体 ID。

这是数据挖掘中最耗时(占80%时间)但最有价值的一环。

👣 下一步

  1. 引入外部知识库 :只靠字符串相似度是不够的(比如"字节跳动"和"今日头条"字面上完全不沾边)。你需要引入工商 API 字典来做语义层面的映射
  2. Deep Learning:训练一个 Siamese Network(孪生神经网络)来判断两个名字是否属于同一家公司,这在企查查等级别的应用中非常常见。

数据清洗不仅是技术,更是一门艺术!希望你的数据永远整洁如新!

🌟 文末

好啦~以上就是本期 《Python爬虫实战》的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥

📌 专栏持续更新中|建议收藏 + 订阅

专栏 👉 《Python爬虫实战》,我会按照"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一篇都做到:

✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)

📣 想系统提升的小伙伴:强烈建议先订阅专栏,再按目录顺序学习,效率会高很多~

✅ 互动征集

想让我把【某站点/某反爬/某验证码/某分布式方案】写成专栏实战?

评论区留言告诉我你的需求,我会优先安排更新 ✅


⭐️ 若喜欢我,就请关注我叭~(更新不迷路)

⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)

⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)


免责声明:本文仅用于学习与技术研究,请在合法合规、遵守站点规则与 Robots 协议的前提下使用相关技术。严禁将技术用于任何非法用途或侵害他人权益的行为。技术无罪,责任在人!!!

相关推荐
喵手2 小时前
Python爬虫实战:国际电影节入围名单采集与智能分析系统:从数据抓取到获奖预测(附 CSV 导出)!
爬虫·python·爬虫实战·零基础python爬虫教学·采集数据csv导出·采集国际电影节入围名单·从数据抓取到获奖预测
派葛穆2 小时前
Python-PyQt5 安装与配置教程
开发语言·python·qt
自可乐3 小时前
Milvus向量数据库/RAG基础设施学习教程
数据库·人工智能·python·milvus
可触的未来,发芽的智生3 小时前
发现:认知的普适节律 发现思维的8次迭代量子
javascript·python·神经网络·程序人生·自然语言处理
Aloudata3 小时前
数据工程视角:指标平台选型深度对比(BI 指标中心 vs 传统 vs Headless vs 自动化平台)
数据分析·自动化·数据治理·指标平台·noetl
真智AI3 小时前
用 LLM 辅助生成可跑的 Python 单元测试:pytest + coverage 覆盖率报告(含运行指令与排坑)
python·单元测试·pytest
0思必得04 小时前
[Web自动化] Selenium处理文件上传和下载
前端·爬虫·python·selenium·自动化·web自动化
Hui Baby4 小时前
Java SPI 与 Spring SPI
java·python·spring
小猪咪piggy4 小时前
【Python】(3) 函数
开发语言·python