㊗️本期内容已收录至专栏《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 标准的训练集。
读完本文,你将获得:
- 🗑️ 垃圾分类能力:编写特定的启发式规则,自动剔除"我不知道"、"帮朋友买的"等无效回答。
- 👯 语义去重逻辑:不只看字面,通过相似度算法识别"防水吗"和"下雨天能穿吗"是同一个问题。
- 💎 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(生产环境推荐)。 - 分词/句 :
NLTK或Jieba。 - 存储 :
JSONL(JSON Lines),这是 OpenAI 微调和各类向量库通用的标准格式。
处理流水线 (The Pipeline):
- Fetcher: 获取原始 JSON 数据。
- Filter: 规则过滤(去除短文本、无效回答)。
- Cluster: 问题聚类(将相似问题归为一组)。
- Select: 挑选最佳答案(点赞数最高 / 字数适中)。
- 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)
-
相似度阈值怎么设?
- 问题:阈值太低(如 0.4),会导致"手机卡吗"和"手机大吗"被合并;阈值太高(0.9),"防水吗"和"防泼溅吗"分不开。
- 建议 :没有银弹。建议先设 0.7,人工抽查 100 条,根据业务场景微调。使用 Embeddings(向量)比
difflib(字符串编辑距离)效果好太多。
-
错别字怎么办(Slang/Typos)?
- 现象 :用户常说"神机 "(神机)、"拔草"。
- 解决 :在清洗前增加一个"同义词替换字典"或"纠错层"。例如:
raw_text.replace("苹菓", "苹果")。
-
如何处理"多轮对话"?
- 有些 Q&A 是追问形式(Reply to Reply)。
- 策略:对于初级语料库,建议只取"一级回答"(Direct Answer),放弃复杂的盖楼回复,保持上下文清晰。
1️⃣1️⃣ 进阶优化(工业级玩法)
如果你的语料库是为了给 ChatGPT / Llama 3 做外挂知识库:
-
Embedding 升级:
- 放弃
difflib。使用sentence-transformers库的m3e-base或bge-large-zh模型。这些模型专门针对中文语义优化,能理解"苹果"和"iPhone"是关联的,而字面匹配做不到。
- 放弃
-
答案摘要(Answer Summarization):
- 如果去重后一个问题下有 10 个有效回答,不要全部存进去。可以调用 LLM(如 GPT-3.5)做一个 Summary:"用户普遍反馈防水性能好,但也有少量反馈掉漆。" 将这个摘要作为最终答案存入库。
-
标签化(Tagging):
- 在 JSONL 中增加
tag字段,如{"tag": "性能"},{"tag": "外观"}。这样在检索时可以加过滤器。
- 在 JSONL 中增加
1️⃣2️⃣ 总结与延伸阅读
复盘:
我们构建了一个数据炼油厂。
- Filter: 像筛沙子一样筛掉无效回答。
- Dedup: 像拼图一样合并相似问题。
- Format: 像打包商品一样输出标准 JSONL。
现在的 knowledge_base.jsonl 文件,可以直接导入 Milvus 或 Elasticsearch,瞬间变成一个懂产品的 AI 客服大脑。🧠
下一步做什么?
- 挑战:尝试抓取一个包含 1000 条 Q&A 的真实页面(如某热门耳机商品)。
- 应用 :使用
LangChain加载这个 JSONL 文件,搭建一个本地问答机器人。
数据是新时代的石油,而清洗就是提炼汽油的过程!加油,数据工程师们!
🌟 文末
好啦~以上就是本期的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥
✅ 专栏持续更新中|建议收藏 + 订阅
墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一期内容都做到:
✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)
📣 想系统提升的小伙伴 :强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集
想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?
评论区留言告诉我你的需求,我会优先安排实现(更新)哒~
⭐️ 若喜欢我,就请关注我叭~(更新不迷路)
⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)
⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)
✅ 免责声明
本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。
使用或者参考本项目即表示您已阅读并同意以下条款:
- 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
- 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
- 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
- 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
