Python爬虫实战:电商问答/FAQ 语料构建 - 去重、分句、清洗,做检索语料等!

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

㊙️本期爬虫难度指数:⭐⭐⭐

🉐福利: 一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。

全文目录:

      • [🌟 开篇语](#🌟 开篇语)
      • [1️⃣ 摘要(Abstract)](#1️⃣ 摘要(Abstract))
      • [2️⃣ 背景与需求(Why)](#2️⃣ 背景与需求(Why))
      • [3️⃣ 合规与注意事项(必写)](#3️⃣ 合规与注意事项(必写))
      • [4️⃣ 技术选型与整体流程(What/How)](#4️⃣ 技术选型与整体流程(What/How))
      • [5️⃣ 环境准备与依赖安装(可复现)](#5️⃣ 环境准备与依赖安装(可复现))
      • [6️⃣ 核心实现:规则清洗器(The Garbageman)](#6️⃣ 核心实现:规则清洗器(The Garbageman))
      • [7️⃣ 核心实现:语义去重与聚合(Semantic Deduper)](#7️⃣ 核心实现:语义去重与聚合(Semantic Deduper))
      • [8️⃣ 核心实现:RAG 语料格式化(Formatter)](#8️⃣ 核心实现:RAG 语料格式化(Formatter))
      • [9️⃣ 运行方式与结果展示(必写)](#9️⃣ 运行方式与结果展示(必写))
      • [🔟 常见问题与排错(FAQ)](#🔟 常见问题与排错(FAQ))
      • [1️⃣1️⃣ 进阶优化(工业级玩法)](#1️⃣1️⃣ 进阶优化(工业级玩法))
      • [1️⃣2️⃣ 总结与延伸阅读](#1️⃣2️⃣ 总结与延伸阅读)
      • [🌟 文末](#🌟 文末)
        • [✅ 专栏持续更新中|建议收藏 + 订阅](#✅ 专栏持续更新中|建议收藏 + 订阅)
        • [✅ 互动征集](#✅ 互动征集)
        • [✅ 免责声明](#✅ 免责声明)

🌟 开篇语

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

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

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

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

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

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

📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。

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

1️⃣ 摘要(Abstract)

本项目致力于将电商平台(如淘宝/亚马逊/京东风格)的原始用户问答数据,转化为可直接用于训练 AI 客服或构建向量知识库(Vector DB)的高质量语料。我们将利用 Python 实现规则清洗、基于语义相似度的去重、以及长文本的分句切片,最终产出符合 jsonl 标准的训练集。

读完本文,你将获得:

  1. 🗑️ 垃圾分类能力:编写特定的启发式规则,自动剔除"我不知道"、"帮朋友买的"等无效回答。
  2. 👯 语义去重逻辑:不只看字面,通过相似度算法识别"防水吗"和"下雨天能穿吗"是同一个问题。
  3. 💎 RAG 语料标准 :学会如何构建 Context-Question-Answer 三元组,为大模型检索做准备。

2️⃣ 背景与需求(Why)

为什么要爬与洗?

  • 真实性:官方详情页(PDP)往往只报喜不报忧,用户 Q&A 包含了真实的优缺点信息。
  • 口语化:用户提问通常非常口语化(Slang),这是训练 Chatbot 理解人类语言的最佳素材。
  • 自动化:构建知识库如果靠人工复制粘贴,一万个 SKU 需要耗费数月,爬虫+清洗脚本只需几小时。

目标字段清单(Schema):

  • sku_id: 商品 ID。
  • category: 商品分类(用于限定检索范围)。
  • question_raw: 原始提问。
  • question_clean: 清洗后的标准化问题。
  • answer_best: 筛选出的最佳回答。
  • retrieval_chunk: 用于存入向量库的文本块(通常是 Q+A 的组合)。

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

电商数据敏感,操作需谨慎。🚦

  • PII 隐私保护:必须抹去用户的用户名、头像链接、订单号等个人隐私信息(Personally Identifiable Information)。我们的语料库里只能有知识,不能有人。
  • 反爬策略(Anti-Scraping):电商平台的 Q&A 接口通常加密严密(如各类 Sign 签名)。建议优先寻找公开的 API,或者使用无头浏览器(Playwright)进行低频采集。
  • 数据用途:仅用于内部模型训练或搜索优化,严禁将抓取的用户评论直接打包出售。

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

技术栈:

  • 清洗 : Pandas + Regex (正则去噪)。
  • 去重 : Difflib (Python标准库,轻量级相似度) 或 Sentence-Transformers (生产环境推荐)。
  • 分词/句 : NLTKJieba
  • 存储 : JSONL (JSON Lines),这是 OpenAI 微调和各类向量库通用的标准格式。

处理流水线 (The Pipeline):

  1. Fetcher: 获取原始 JSON 数据。
  2. Filter: 规则过滤(去除短文本、无效回答)。
  3. Cluster: 问题聚类(将相似问题归为一组)。
  4. Select: 挑选最佳答案(点赞数最高 / 字数适中)。
  5. Format: 格式化为检索语料。

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

Python 版本: 3.9+

依赖安装:

bash 复制代码
pip install pandas requests tqdm nltk

(注:为了保证代码人人能跑,本演示使用 Python 内置的 difflib 做相似度计算,生产环境建议安装 sentence-transformers 使用 BERT 模型)

目录结构:

text 复制代码
qa_forge/
├── raw_data/          # 模拟的原始抓取数据
├── dataset/
│   └── knowledge_base.jsonl # 最终产出
└── processor.py       # 主程序

6️⃣ 核心实现:规则清洗器(The Garbageman)

电商问答里最大的问题是噪音。比如有人问"好用吗?",有人回"还没拆封,不知道"。这种数据进了知识库就是灾难。

python 复制代码
import re

class QACleaner:
    def __init__(self):
        # 编译正则,提高效率
        # 过滤掉包含这些关键词的无效回答
        self.garbage_patterns = [
            re.compile(r'不清楚|不知道|没买|送人|帮朋友|未拆封'),
            re.compile(r'习惯好评|系统默认'),
            re.compile(r'客服|联系卖家') # 这种回答没有知识价值
        ]
        # 清洗 HTML 标签
        self.html_tag = re.compile(r'<[^>]+>')
        # 去除连续标点
        self.punctuation = re.compile(r'[!!??.。,,]{2,}')

    def clean_text(self, text):
        """基础文本清洗"""
        if not text:
            return ""
        text = self.html_tag.sub('', text) # 去标签
        text = self.punctuation.sub('。', text) # 标点标准化
        return text.strip()

    def is_valid_answer(self, answer_text):
        """
        启发式规则判断回答是否有效
        1. 长度不能太短(小于4个字通常没意义)
        2. 不能包含无效关键词
        """
        if len(answer_text) < 4:
            return False
            
        for pattern in self.garbage_patterns:
            if pattern.search(answer_text):
                return False
                
        return True

7️⃣ 核心实现:语义去重与聚合(Semantic Deduper)

同一个 SKU 下,用户会反复问同一个问题。我们需要把它们合并,避免知识库冗余。

python 复制代码
import difflib

class SemanticDeduper:
    def __init__(self, threshold=0.7):
        self.threshold = threshold

    def get_similarity(self, s1, s2):
        """
        计算两个字符串的相似度 (0.0 - 1.0)
        这里使用 SequenceMatcher,生产环境建议用 BERT Embedding
        """
        return difflib.SequenceMatcher(None, s1, s2).ratio()

    def deduplicate_questions(self, qa_list):
        """
        对问题列表进行去重和聚合
        输入: [{'q': '...', 'a': '...'}]
        输出: 聚合后的唯一问题列表
        """
        unique_qas = []
        
        for item in qa_list:
            current_q = item['question']
            is_duplicate = False
            
            # 与已有的唯一问题库进行比对
            for existing in unique_qas:
                sim = self.get_similarity(current_q, existing['question'])
                if sim > self.threshold:
                    # 发现重复!
                    # 策略:保留更长、更详细的那个问题描述,或者把答案合并
                    # 这里演示简单的跳过策略(或者可以把新答案追加到旧问题下)
                    existing['answers'].extend(item['answers'])
                    is_duplicate = True
                    break
            
            if not is_duplicate:
                unique_qas.append({
                    'question': current_q,
                    'answers': item['answers'] # 这里的 answers 是一个列表
                })
                
        return unique_qas

8️⃣ 核心实现:RAG 语料格式化(Formatter)

这一步是将清洗好的数据,组装成 RAG(检索增强生成)需要的格式。通常是 Input (用户问题) + Context (作为知识检索的文本块)。

python 复制代码
import json

class RAGFormatter:
    def export_jsonl(self, data, filename="knowledge_base.jsonl"):
        """
        导出为 JSONL 格式,这是大模型微调和向量库的标准格式
        每一行是一个完整的 JSON 对象
        """
        with open(filename, 'w', encoding='utf-8') as f:
            for item in data:
                # 选取最佳答案(这里简化为选第一个,实际可根据点赞数选)
                best_answer = item['answers'][0] if item['answers'] else ""
                
                if not best_answer:
                    continue

                # 构建检索块 (Chunk):通常是 "问题:xxx 答案:xxx"
                # 这样检索时,无论搜问题还是搜答案关键词都能命中
                retrieval_chunk = f"问题:{item['question']}\n回答:{best_answer}"
                
                record = {
                    "text": retrieval_chunk, # 用于 Embedding 的文本
                    "metadata": {
                        "source": "customer_qa",
                        "question": item['question'],
                        "answer": best_answer
                    }
                }
                
                f.write(json.dumps(record, ensure_ascii=False) + "\n")
        print(f"💾 Saved {len(data)} QA pairs to {filename}")

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

为了让代码可运行,我们模拟了一组电商原始数据(Raw Data)。

python 复制代码
# --- 模拟原始抓取数据 ---
MOCK_RAW_DATA = [
    {"q": "这款手机防水吗?", "a": "IP68级防水,可以下水。", "likes": 10},
    {"q": "下雨天能用吗?", "a": "可以的,防泼溅。", "likes": 2}, # 语义重复
    {"q": "防水效果如何", "a": "不知道,帮人买的。", "likes": 0}, # 垃圾回答
    {"q": "这手机打王者荣耀卡吗?", "a": "不卡,非常流畅,120帧满跑。", "likes": 50},
    {"q": "打游戏发热严重吗?", "a": "玩久了有点温热,能接受。", "likes": 5},
    {"q": "只有耳机插孔吗", "a": "no", "likes": 1} # 过短,可能需要过滤或保留
]

def main():
    cleaner = QACleaner()
    deduper = SemanticDeduper(threshold=0.6) # 设定相似度阈值
    formatter = RAGFormatter()
    
    print("🚀 QA-Forge Pipeline Started...\n")
    
    # 1. 预处理:将扁平数据转为结构化,并初步清洗
    pre_processed = []
    for item in MOCK_RAW_DATA:
        clean_q = cleaner.clean_text(item['q'])
        clean_a = cleaner.clean_text(item['a'])
        
        # 垃圾回答过滤
        if not cleaner.is_valid_answer(clean_a):
            print(f"🗑️ Dropped Garbage: Q:{clean_q} | A:{clean_a}")
            continue
            
        # 临时结构
        pre_processed.append({
            "question": clean_q,
            "answers": [clean_a] # 初始时一对一
        })

    # 2. 语义去重
    print("\n🔍 Deduplicating similar questions...")
    unique_data = deduper.deduplicate_questions(pre_processed)
    
    print(f"✅ Deduplication result: {len(pre_processed)} -> {len(unique_data)}")
    
    # 3. 结果展示
    print("\n📝 Sample Knowledge Base:")
    for idx, item in enumerate(unique_data):
        print(f"[{idx+1}] Q: {item['question']}")
        print(f"    A: {item['answers']}") # 注意这里可能合并了多个答案

    # 4. 导出
    formatter.export_jsonl(unique_data)

if __name__ == "__main__":
    main()

运行结果示例:

text 复制代码
🚀 QA-Forge Pipeline Started...

🗑️ Dropped Garbage: Q:防水效果如何 | A:不知道,帮人买的。
🗑️ Dropped Garbage: Q:只有耳机插孔吗 | A:no

🔍 Deduplicating similar questions...
✅ Deduplication result: 4 -> 3

📝 Sample Knowledge Base:
[1] Q: 这款手机防水吗?
    A: ['IP68级防水,可以下水。', '可以的,防泼溅。']  <-- 成功将"下雨天能用吗"合并到这里!
[2] Q: 这手机打王者荣耀卡吗?
    A: ['不卡,非常流畅,120帧满跑。']
[3] Q: 打游戏发热严重吗?
    A: ['玩久了有点温热,能接受。']

💾 Saved 3 QA pairs to knowledge_base.jsonl

🔟 常见问题与排错(FAQ)

  1. 相似度阈值怎么设?

    • 问题:阈值太低(如 0.4),会导致"手机卡吗"和"手机大吗"被合并;阈值太高(0.9),"防水吗"和"防泼溅吗"分不开。
    • 建议 :没有银弹。建议先设 0.7,人工抽查 100 条,根据业务场景微调。使用 Embeddings(向量)比 difflib(字符串编辑距离)效果好太多。
  2. 错别字怎么办(Slang/Typos)?

    • 现象 :用户常说"神机 "(神机)、"拔草"。
    • 解决 :在清洗前增加一个"同义词替换字典"或"纠错层"。例如:raw_text.replace("苹菓", "苹果")
  3. 如何处理"多轮对话"?

    • 有些 Q&A 是追问形式(Reply to Reply)。
    • 策略:对于初级语料库,建议只取"一级回答"(Direct Answer),放弃复杂的盖楼回复,保持上下文清晰。

1️⃣1️⃣ 进阶优化(工业级玩法)

如果你的语料库是为了给 ChatGPT / Llama 3 做外挂知识库:

  • Embedding 升级

    • 放弃 difflib。使用 sentence-transformers 库的 m3e-basebge-large-zh 模型。这些模型专门针对中文语义优化,能理解"苹果"和"iPhone"是关联的,而字面匹配做不到。
  • 答案摘要(Answer Summarization)

    • 如果去重后一个问题下有 10 个有效回答,不要全部存进去。可以调用 LLM(如 GPT-3.5)做一个 Summary:"用户普遍反馈防水性能好,但也有少量反馈掉漆。" 将这个摘要作为最终答案存入库。
  • 标签化(Tagging)

    • 在 JSONL 中增加 tag 字段,如 {"tag": "性能"}, {"tag": "外观"}。这样在检索时可以加过滤器。

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

复盘:

我们构建了一个数据炼油厂

  1. Filter: 像筛沙子一样筛掉无效回答。
  2. Dedup: 像拼图一样合并相似问题。
  3. Format: 像打包商品一样输出标准 JSONL。

现在的 knowledge_base.jsonl 文件,可以直接导入 MilvusElasticsearch,瞬间变成一个懂产品的 AI 客服大脑。🧠

下一步做什么?

  • 挑战:尝试抓取一个包含 1000 条 Q&A 的真实页面(如某热门耳机商品)。
  • 应用 :使用 LangChain 加载这个 JSONL 文件,搭建一个本地问答机器人。

数据是新时代的石油,而清洗就是提炼汽油的过程!加油,数据工程师们!

🌟 文末

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

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

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

墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一期内容都做到:

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

📣 想系统提升的小伙伴 :强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集

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

评论区留言告诉我你的需求,我会优先安排实现(更新)哒~


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

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

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


✅ 免责声明

本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。

使用或者参考本项目即表示您已阅读并同意以下条款:

  • 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
  • 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
  • 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
  • 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
相关推荐
Dxy12393102161 小时前
DataFrame条件筛选:从入门到实战的数据清洗利器
python·dataframe
musenh1 小时前
python基础
开发语言·windows·python
清水白石0081 小时前
解锁 Python 性能潜能:从基础精要到 `__getattr__` 模块级懒加载的进阶实战
服务器·开发语言·python
清水白石0082 小时前
缓存的艺术:Python 高性能编程中的策略选择与全景实战
开发语言·数据库·python
AI Echoes2 小时前
对接自定义向量数据库的配置与使用
数据库·人工智能·python·langchain·prompt·agent
得一录2 小时前
LoRA(Low-Rank Adaptation)的原理和实现
python·算法·机器学习
喵手2 小时前
Python爬虫实战:同名实体消歧 - 店铺/公司名规则合并与标准化等!
爬虫·python·爬虫实战·零基础python爬虫教学·同名实体消歧·店铺/公司名规则合并与标准化
七夜zippoe2 小时前
集成测试实战:构建可靠的测试金字塔体系
python·log4j·e2e·fastapi·持续集成·flask api
yunhuibin2 小时前
VGGNet网络学习
人工智能·python·深度学习·神经网络·学习