Open-Assistant 知乎 KOL 问答数据集构建指南:爬取、清洗、转换与上传全流程
【免费下载链接】Open-AssistantOpenAssistant is a chat-based assistant that understands tasks, can interact with third-party systems, and retrieve information dynamically to do so.项目地址: https://gitcode.com/gh_mirrors/op/Open-Assistant
导读
本文围绕 Open-Assistant 项目中的中文高质量问答数据源 Zhihu KOL 数据集卡片 展开,完整讲解如何从知乎抓取 KOL(关键意见领袖)的问答内容,将其转换为 Open-Assistant 训练管线可直接消费的 Instruction 格式,并以 Parquet 形式发布到 Hugging Face Hub。读完本文,你将掌握单用户 url_token 爬取、圆桌话题批量爬取、数据格式转换、质量过滤与上传发布的全套实战方案,并能将这套流程复用到其他中文问答站点或社区数据的采集任务中。
数据集概览:为什么需要知乎 KOL 问答数据
背景与定位
Open-Assistant 旨在训练一个能够理解任务、与第三方系统交互并动态检索信息的对话式助手。高质量、多样化的训练数据是其核心燃料,仓库根目录的 data/datasets/README.md 明确指出:该仓库致力于提供可用于训练 OpenAssistant 模型的多样化、易获取的数据集集合,覆盖广泛的主题、语言与任务。
知乎(Zhihu)是中国最流行的问答社区,天然包含海量的「问题—回答」二元组,这与 Instruction 数据集的形态高度契合。本数据集当前阶段聚焦 KOL(Key Opinion Leader,意见领袖)的回答,原因在于:
- KOL 回答通常更长、更结构化、信息密度更高,更适合作为监督微调的目标输出;
- KOL 内容的点赞数等互动指标可以反哺元数据(如回答质量筛选);
- 项目计划后续逐步扩展到更广泛的话题和普通用户内容,当前版本只收录 KOL 数据作为起步。
从仓库注册表 data/datasets/init.py 可以看到,zhihu-kol已被注册到INSTRUCTION_DATASETS字典中,对应 Hugging Face 仓库wangrui6/zhihu-kol,即该数据集已经进入 Open-Assistant 的正式指令数据集清单。
数据形态示例
数据集的「问题—回答」对直接以中文呈现,例如 README 中给出的两个典型样本:
Q: ChatGPT 这个项目会开源吗? A: 别开源了,开源就有中国名字了: HarmonyGPT.2014年6月21日,特斯拉公司在全球范围内公开了271项专利... Q: TensorFlow 真的要被 PyTorch 比下去了吗? A: 当我找到一个tf的代码,我的第一反应就是这货大概率跑不起来.这类样本真实反映了社区用户的提问风格和 KOL 的个性化回答风格,是训练中文指令跟随能力(尤其是互联网技术话题)的天然语料。
环境准备:依赖安装与 Playwright 浏览器
开始抓取前需要准备两件事:Python 依赖和 Playwright 无头浏览器。
安装 Python 依赖
项目提供了完整的依赖清单 data/datasets/zhihu-kol/requirements.txt,直接安装即可:
pip install -r requirements.txt其中与抓取链路直接相关的核心依赖包括:
| 依赖 | 版本 | 用途 |
|---|---|---|
playwright | 1.31.0 | 无头浏览器驱动,用于圆桌话题页面渲染与链接采集 |
requests | 2.28.2 | 知乎 API 与页面请求 |
beautifulsoup4 | 4.11.2 | HTML 解析,提取回答正文 |
pandas | 1.5.3 | 数据表处理与 CSV 输出 |
pyarrow | 11.0.0 | Parquet 格式转换 |
datasets | 2.10.0 | Hugging Face 数据集加载与上传 |
huggingface-hub | 0.12.1 | HF Hub 推送底层支持 |
multitasking | 0.0.11 | 多任务并发抓取回答内容 |
retry | 0.9.2 | 网络请求自动重试 |
tqdm | 4.64.1 | 抓取进度可视化 |
loguru | 0.6.0 | 圆桌抓取日志记录 |
注意requirements.txt中还捆绑了大量 Jupyter/科学计算依赖(如jupyter、matplotlib、tensorboard等),实际生产抓取时可按需裁剪。
安装 Playwright 浏览器及系统库
playwright install若系统缺少浏览器运行所需的基础库,需要补充安装(以 Ubuntu/Debian 为例):
sudo apt-get install libatk1.0-0 libatk-bridge2.0-0 libcups2 libatspi2.0-0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libxkbcommon0 libpango-1.0-0 libcairo2 libasound2这些库为 Chromium 在无头(headless)模式下渲染页面所必需;缺少时 Playwright 会因启动浏览器失败而报错。注意,在scrape_by_topic.py中浏览器以headless=False(有头模式)启动,这是因为知乎页面需要真实浏览器环境来通过部分反爬校验。
单用户抓取:基于 url_token 的 KOL 回答采集
url_token 与 uid 的获取
知乎用户的个人主页 URL 形如https://www.zhihu.com/people/la-ge-lang-ri-96-69,其中最后一段la-ge-lang-ri-96-69即为该用户的url_token。抓取的第一步是将其转换为 API 所需的数字uid。
main.py 中的get_uid_by_url_token函数实现这一转换:它构造一组模拟浏览器请求的 headers(包含user-agent、x-requested-with: fetch、origin、referer等字段),请求https://api.zhihu.com/people/{url_token},并从响应的 JSON 中提取id字段:
url = "https://api.zhihu.com/people/" + url_token response = requests.get(url, headers=headers) uid = response.json()["id"]由于知乎对无登录 API 请求存在限流,该函数未做重试保护;若调用失败,get_user_answers外层会捕获异常并返回空 DataFrame,脚本随后打印「url_token 可能有误!」并退出。
拉取用户回答元数据列表
get_user_answers(url_token, max_count=100000)是单用户抓取的核心。它使用 iOS 端请求头模拟知乎 App 客户端,调用分页接口:
url = f"https://api.zhihu.com/members/{uid}/answers" params = (("limit", "20"), ("offset", str(offset)))关键行为包括:
- 分页逻辑:每页
limit=20条,通过offset递增翻页,直到offset >= total或超过max_count(默认 100000,即几乎不设上限)时停止; - 自动重试:函数上装饰了
@retry(tries=3),网络异常时最多重试 3 次; - 字段提取:通过
operations映射表,从原始 JSON 中抽取 9 个字段(含嵌套字段),列名全部使用中文:
| 输出列名 | 来源字段 | 说明 |
|---|---|---|
| 作者名称 | author.name | KOL 显示名 |
| 作者ID | author.id | KOL 数字 uid |
| 作者token | author.url_token | KOL url_token |
| 回答点赞数 | voteup_count | 可用于回答质量评估 |
| 回答时间 | created_time | 创建时间戳 |
| 更新时间 | updated_time | 最后更新时间戳 |
| 回答ID | url末段 | 回答唯一 ID(aid) |
| 问题ID | question.id | 问题唯一 ID(qid) |
| 问题内容 | question.title | 问题标题文本 |
其中「回答ID」通过x["url"].split("/")[-1]从回答链接中截取;「问题内容」直接取自问题的 title。该函数返回一个列名均为中文的 pandas DataFrame,为后续正文抓取提供索引。
抓取回答正文
拿到「问题ID + 回答ID」后,get_answer_content(qid, aid)通过移动端页面请求https://www.zhihu.com/question/{qid}/answer/{aid},再用 BeautifulSoup 解析 HTML:
soup = BeautifulSoup(response.text, "html.parser") content = " ".join([p.text.strip() for p in soup.find_all("p")])即将页面内所有<p>段落文本拼接成一段连续文本。需要注意:这种粗暴拼接会丢失段落结构与列表、代码块等富文本信息;同时 README 示例中可看到回答内容会保留原始换行与排版,实际以页面解析结果为准。
并发落盘与格式转换
save_answers_to_csv将上述两步串联成一个端到端流程:
- 调用
get_user_answers获取元数据 DataFrame,为空则报错返回; - 对每一行「问题ID + 回答ID」组合,使用
multitasking.task装饰的start(qid, aid)并发抓取正文,每个任务带@retry(tries=3)重试; - 通过
tqdm显示进度,multitasking.wait_for_tasks()等待全部并发任务结束; - 按
问题ID回填回答内容(源码注释特别强调使用 qid 作为 key,确保并发下 qid 与 aid 一一对应); - 调用
reformat_csv_to_openassistant转换为 Open-Assistant 标准格式:INSTRUCTION← 问题内容RESPONSE← 回答内容SOURCE← 固定为"Zhihu"METADATA← JSON 字符串,包含「回答点赞数」和「回答时间」两项
- 以
encoding="utf-8-sig"编码写入 CSV(带 BOM,便于 Excel 等工具直接打开中文内容)。
脚本主入口默认抓取 url_token 为nicole-97-93的 KOL 并保存为nicole-97-93.csv,实际使用时可修改main.py底部的url_token变量:
# 知乎用户的 url_token # 例如主页为 : https://www.zhihu.com/people/la-ge-lang-ri-96-69 的用户 # 其 url_token 为 la-ge-lang-ri-96-69 # url_token = 'la-ge-lang-ri-96-69' url_token = "nicole-97-93" # 回答数据保存路径 csv_path = url_token + ".csv" # 调用函数获取数据 save_answers_to_csv(url_token, csv_path)运行命令(对应 README 的「Download data based on single KOL url token」):
python main.py圆桌话题批量抓取:Playwright 端到端自动化
单用户模式需要逐个提供 url_token,扩展性有限。为此 scrape_by_topic.py 提供了基于知乎「圆桌」(roundtable)话题的批量抓取方案,README 对应命令为:
python scrape_by_topic.py抓取思路
圆桌是知乎围绕某一主题聚合问答的专题页面(如科技、职场等),页面中包含大量与主题相关的问题和回答链接。脚本借助 Playwright 的真实浏览器能力实现端到端自动化,核心流程如下:
- 进入圆桌列表页:
page.goto("https://zhihu.com/roundtable"),通过roundtable_topic_scrolldown = 20次End键滚动加载更多圆桌主题(每次滚动后wait_for_timeout(1000)); - 提取主题链接:
get_all_href(page)使用page.evaluate执行页面内 JS,收集全部href,过滤出包含https://www.zhihu.com/roundtable/的链接,并np.random.shuffle随机打乱顺序; - 跳过早期主题:
starting_offset = 4,跳过列表前 4 个主题——源码注释说明早期圆桌话题可能尚未正式开始,偏移量是任意设定的; - 逐主题进入:对每个主题页收集所有
href,从中过滤出含/people/的用户主页链接(scrape_people_roundtable函数会将其累计写入people.csv,用于反推 KOL 名单); - 逐问题抓取回答:
end_to_end_auto_scrape函数进一步遍历主题页上的问题链接(过滤掉含waiting的未开始问题),进入每个问题页:- 用
.QuestionHeader-title选择器读取问题标题; - 在
.QuestionAnswers-answers容器内收集所有href,用正则r"/question/\d+/answer/\d+"匹配出「问题ID/回答ID」对; - 对每个回答调用
get_answer_content(qId, aId, question_title)抓取正文,任务间time.sleep(1)限速以降低封禁风险;
- 用
- 增量落盘:每处理完一个主题,就把累积的 payload 用
pd.json_normalize展平并写入zhihu.csv,做到边抓边存、断点可续。
富化的回答数据结构
圆桌流程使用的get_answer_content返回的是Content_Data数据类,字段比单用户版本更丰富:
@dataclass class Content_Data: question_id: int answer_id: int author_id: str question_title: str content: str upvotes: str answer_creation_time: str其中额外字段的提取方式体现了对页面元数据的挖掘:
- 回答创建时间:从
<meta itemprop="dateCreated">标签中读取(源码注释说明经在线页面人工核对,该 meta 内容即回答创建时间); - 点赞数:查找
class="Button VoteButton VoteButton--up"按钮文本,并清理零宽字符\u200b; - 作者 ID:遍历
<meta itemprop="url">标签,筛出 content 中含/people/的那一个。
这些字段在后续 Parquet 转换时会被写入 METADATA,为数据筛选(例如按点赞数过滤低质量回答)提供依据。
有头模式的取舍
脚本中headless = False,即 Playwright 以有头模式运行。虽然无头模式更省资源,但知乎对纯无头浏览器的反爬识别更严格;有头模式配合真实 UA 能显著提高抓取成功率。代价是需要图形环境(桌面或带显示的容器),在纯服务器上运行可能需要配合xvfb之类的虚拟显示方案,这一点在 README 中并未展开,属于从代码推断的部署注意事项。
数据标准化:转换为 Open-Assistant Parquet 格式
抓取产物是 CSV(zhihu.csv或{url_token}.csv),而训练管线需要标准化的 Parquet 文件。convert_parquet.py 完成这一转换,对应 README 命令:
python convert_parquet.pyInstruction 格式规范
根据 data/datasets/README.md 的统一定义,Instruction 数据集必须包含以下列:
- INSTRUCTION(字符串):指令文本(此处即知乎问题标题);
- RESPONSE(字符串):期望的回复(此处即 KOL 回答正文);
- SOURCE(字符串):原始数据源简称,此处固定为
"Zhihu"; - METADATA(JSON 字符串,可选):其他有用信息,如 NSFW 标记
{"nsfw": true}。
同时,仓库对数据格式有硬性要求:所有数据必须是 UTF-8 编码;存储为 Parquet 时必须使用row_group_size=100且index=False(jsonl / jsonl.gz 亦可)。
转换实现
reformat_csv_to_openassistant将圆桌抓取的 CSV 映射为标准四列,METADATA 中保留 5 个溯源字段:
new_df["METADATA"] = df.apply( lambda x: json.dumps( { "question_id": x["question_id"], "answer_id": x["answer_id"], "author_id": x["author_id"], "upvotes": x["upvotes"], "answer_creation_time": x["answer_creation_time"], }, ensure_ascii=False, ), axis=1, )值得注意的是,转换阶段还内置了一道质量过滤:
new_df = new_df[~(new_df["RESPONSE"] == " ") | (new_df["RESPONSE"].isna())]该行意图剔除回答内容为空(单个空格)或缺失的行,避免无效样本进入训练集。严格来说,按逻辑运算符优先级这里存在一个易混淆点(应为「剔除 RESPONSE 为空格或为 NaN 的行」),但从意图看这是一道明确的数据清洗步骤,读者可依据需求自行调整过滤条件。
主流程将zhihu.csv读入,转换后写为 Parquet:
df = pd.read_csv(input_csv) df = reformat_csv_to_openassistant(df) df.to_parquet("dataset.parquet", row_group_size=100, engine="pyarrow", index=False)这正好与 data/datasets/README.md 给出的全仓库统一 Parquet 转换范式(df.to_parquet("dataset.parquet", row_group_size=100, engine="pyarrow", index=False))完全一致,说明该数据集严格遵守了 Open-Assistant 的数据规范。
发布到 Hugging Face:上传数据集
上传脚本
upload_hf.py 极其精简,仅 4 行:
from datasets import Dataset ds = Dataset.from_parquet("dataset.parquet") ds.push_to_hub("wangrui6/zhihu-kol")Dataset.from_parquet将 Parquet 加载为 HF Datasets 对象,push_to_hub将其推送到wangrui6/zhihu-kol仓库(与 data/datasets/init.py 注册表中的目标一致)。对应 README 命令:
python upload_hf.py前置登录与规范要点
在执行上传前,需要先完成 Hugging Face 身份认证。依据 data/datasets/README.md 的通用流程:
pip install huggingface_hub huggingface-cli login # 使用你的 access token 登录登录成功后运行上传脚本即可。注意push_to_hub需要写入权限的 token(Write 级别),只读 token 无法完成推送。
另外两个需要留意的仓库规范:
- 数据集许可:数据集必须具有宽松许可证(permissive license);
- 隐私红线:不得包含儿童性虐待材料,不得包含个人隐私信息(姓名、地址、电话、政府 ID、医疗信息等)。
知乎 KOL 回答属于社区公开内容,但抓取与再发布时仍应自行评估内容合规性与授权边界——这是 README 未明说但值得实践者关注的事项。
与训练管线的衔接:数据集如何被消费
该数据集并非孤立存在,它已经被 Open-Assistant 的训练代码正式引用。在 model/model_training/custom_datasets/instruction.py 的INSTRUCTION_DATASETS注册表中可以看到:
"zhihu-kol": {"dataset_path": "wangrui6/zhihu-kol"},InstructionDataset 加载逻辑
训练端通过InstructionDataset类消费该数据集,其关键行为包括:
- 列名约定:默认读取
INSTRUCTION与RESPONSE两列(构造函数参数instruction_column="INSTRUCTION"、response_column="RESPONSE"),这正好对应本数据集转换脚本产出的列名; - 空值过滤:加载时丢弃
q/a为空或 strip 后长度为 0 的样本,并统计num_invalid打印警告; - 词级过滤:调用
_filter_by_words对问题与回答进行单词级过滤(见 model/model_training/custom_datasets/utils.py),剔除不适合训练的内容; - 随机打乱与拼包:以
seed=42打乱样本顺序,并按fill_min_length将多条问答拼接成训练块(block),满足 SFT 的序列长度要求; - 数据格式适配:
__getitem__通过create_dataset_entry_qa(mode=..., questions=..., answers=..., lang=...)生成DatasetEntry。注意注册表中zhihu-kol未指定lang字段(对比recipes、grade_school_math_instructions等显式标注了"lang": "en"),因此中文内容不会被子采样逻辑按语言过滤——从源码结构可以推断,该数据集在训练时按默认语言处理路径参与混入。
数据流总结
至此,整个「知乎 KOL 问答数据」从采集到训练的全链路可以概括为:
知乎页面/API │ main.py / scrape_by_topic.py(requests + Playwright + BeautifulSoup) ▼ CSV(中文列名 or Content_Data 结构) │ convert_parquet.py(reformat + 空内容过滤) ▼ dataset.parquet(INSTRUCTION / RESPONSE / SOURCE / METADATA) │ upload_hf.py(push_to_hub) ▼ HF Hub: wangrui6/zhihu-kol │ model_training/custom_datasets/instruction.py(InstructionDataset) ▼ Open-Assistant SFT/RL 训练管线这条链路完整覆盖了 Open-Assistant 数据贡献规范(data/datasets/README.md)中「创建数据集 → 转 Parquet → 登录 HF → 推送 → 注册到__init__.py」的每一步,zhihu-kol已注册在 data/datasets/init.py 的INSTRUCTION_DATASETS中,是一个端到端跑通的中文指令数据范例。
总结与扩展建议
围绕 data/datasets/zhihu-kol/README.md 描述的知乎 KOL 数据集,仓库提供了两套互补的抓取方案:
- 单用户模式(
main.py):适合已确定 KOL 名单、按人精准采集,输出含 9 个中文元数据列的 CSV; - 圆桌批量模式(
scrape_by_topic.py):适合冷启动阶段从圆桌话题自动发现问题与回答,通过 Playwright 有头浏览器规避反爬,边抓边存。
两条路径最终都汇入统一的 Open-Assistant Instruction 标准格式,经 Parquet 化后上传 HF,并被训练管线原生消费。若需扩展该方案,可以关注以下几个方向:
- 扩充 KOL 名单:结合
scrape_people_roundtable产出的people.csv积累更多作者,再回灌到main.py逐个精抓; - 丰富元数据:
Content_Data中的upvotes可作为回答质量的代理指标,在convert_parquet.py中按阈值过滤低互动内容; - 补充格式规范:当前正文提取仅拼接
<p>文本,若需要代码块、公式等富文本结构,可基于 BeautifulSoup 进一步增强解析规则; - 合规与隐私:数据再发布前需自行评估知乎内容的使用授权与个人信息脱敏。
对于希望在 Open-Assistant 上复刻中文问答数据采集流程的开发者,本数据集从抓取、清洗、转换到发布的完整代码(main.py、scrape_by_topic.py、convert_parquet.py、upload_hf.py)提供了可直接参考或移植的成熟范式。
【免费下载链接】Open-AssistantOpenAssistant is a chat-based assistant that understands tasks, can interact with third-party systems, and retrieve information dynamically to do so.项目地址: https://gitcode.com/gh_mirrors/op/Open-Assistant
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考