构建 OpenAssistant 影视剧台词对话语料:tv_dialogue 数据集从抓取到统一格式化的完整工程实践
2026/9/19 22:23:22 网站建设 项目流程

构建 OpenAssistant 影视剧台词对话语料:tv_dialogue 数据集从抓取到统一格式化的完整工程实践

【免费下载链接】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 的重要语料来源。本文以 OpenAssistant 仓库中的 tv_dialogue 数据集为研究对象,完整讲解其设计动机、统一的数据格式规范、覆盖 9 类影视作品/电影的来源构成,以及从公开渠道下载、清洗、结构化、导出 Parquet 的全套可复现代码流程,读者学完后即可按同样方法构建属于自己的多来源对话数据集。

该数据集位于仓库 data/datasets/tv_dialogue,包含 README.md(数据卡片)、两个 Jupyter Notebook(public.ipynb 处理 Friends、The Office、Marvel、Doctor Who、Star Trek 五个"纯对话"来源;imsdb.ipynb 负责从 imsdb.com 抓取带详细场景描述的完整剧本)以及 requirements.txt 依赖清单。

一、数据集定位:为什么要为对话模型收集影视台词

对话数据集通常来自三大渠道:社交平台用户发言、机器生成对话、以及影视剧本/字幕。前两者或噪音较多,或缺乏多轮对话的自然结构。影视剧本则天然具备以下优势:

  • 多角色轮转结构:每句台词带明确的说话人标注,天然形成[说话人] 台词的交替结构,是训练多轮对话模型的理想输入;
  • 口语化表达密度高:剧本语言贴近真实交流,包含大量省略、停顿、语气词,有助于模型学习自然语言节奏;
  • 场景上下文丰富:台词依附于具体场景,场景切换、画外音(v.o.)等标注为理解对话语境提供了辅助信息;
  • 来源公开可获取:本数据集的全部剧本均来自公开的 GitHub 仓库、Kaggle 数据集和剧本网站,工程上可复现。

正因如此,tv_dialogue 将"影视剧对话/剧本"作为对话语料的一个独立来源,按照 OpenAssistant 的语料组织方式(每条样本即一集/一部电影)进行统一整理。

二、数据集规模与 YAML 数据卡片解读

数据卡片头部(dataset_info块)从 HuggingFace Datasets 的视角声明了数据集的 Schema,是理解整个数据集结构的第一份官方文档:

dataset_info: features: - name: TEXT dtype: string - name: METADATA dtype: string - name: SOURCE dtype: string splits: - name: train num_bytes: 211728118 num_examples: 2781 download_size: 125187885 dataset_size: 211728118 license: mit task_categories: - conversational - text2text-generation language: - en tags: - OpenAssistant - transcripts - subtitles - television pretty_name: TV and Movie dialogue and transcript corpus size_categories: - 1K<n<10K

要点拆解:

字段含义
featuresTEXT / METADATA / SOURCE每条样本三个字段,均为字符串
splits.train2781 条 / 211,728,118 字节单个 train 划分,数据集约 2.1 亿字节
download_size125,187,885 字节原始文件压缩下载体积约 1.25 亿字节
licensemitMIT 开源协议
task_categoriesconversational, text2text-generation对话与文本生成两类任务均可使用
languageen当前仅收录英语语料
size_categories1K<n<10K样本量在 1000 到 10000 之间

三个特征列的分工明确:TEXT是经过统一格式化后的完整剧本正文,METADATA是该集/该片的结构化信息(JSON 字符串),SOURCE则记录原始出处(格式为来源组织/抓取者,如friends/emorynlp),保证每条语料可溯源。

三、统一数据格式:一集一行,[说话人] 台词

README 中给出了格式规范的官方示例:

[PERSON 1] Hello [PERSON 2] Hello Person 2! How's it going? (they are both talking) [PERSON 1] I like being an example on Huggingface! They are examples on Huggingface. CUT OUT TO ANOTHER SCENCE We are somewhere else [PERSON 1 (v.o)] I wonder where we are?

从示例可以归纳出这条数据集的格式化约定:

  1. 角色标注:每句对白以[说话人]前缀开始;
  2. 多行台词:同一说话人的连续多行台词在下一个[前持续累积(如[PERSON 2]的两行内容);
  3. 场景信息以原文保留:如(they are both talking)这样的动作提示、CUT OUT TO ANOTHER SCENCE这样的场景切换说明,作为场景上下文留在文本中,而非被粗暴删除;
  4. 特殊说话人标记[PERSON 1 (v.o)]中的(v.o)表示画外音(voice-over),说明该台词属于非画面内的画外叙述;
  5. 每行一条样本:数据集以"一集 / 一部电影"为一行(共 2781 行),而不是以单句对白为行——这样每条样本天然是一段完整、有开头结尾的多轮对话。

值得注意的是,这种"粗粒度一行"的设计与后续 OpenAssistant 的训练数据组织方式一致:先以完整剧本为单位组织语料,再在训练阶段由数据加载器按需要切分成对话片段。仓库中的相关加载逻辑可参考 data/datasets/init.py 等数据集入口文件。

四、语料来源全景:两类剧本、九大出处

README 将全部来源分成两类,并附上了原始出处链接,本文按仓库文档整理如下(原始链接见 README 原文,此处保留来源标识供追溯):

4.1 纯对话类:仅保留台词,附带少量场景信息

剧集剧本数抓取来源Source 标识
Friends236 集character-mining 项目(emorynlp)friends/emorynlp
The Office186 集Kaggle 数据集 nasirkhalid24office/nasirkhalid24
Marvel Cinematic Universe18 部电影Kaggle 数据集 pduntonmarvel/pdunton
Doctor Who306 集Kaggle 数据集 jeanmidevdrwho/jeanmidev
Star Trek708 集chakoteya.net,基于 Star_Trek_Scripts 项目整理startrek/chakoteya

这一类的特点是:原始数据本身已具备结构化字段(说话人、季、集、场景),加工重点是合并字段、过滤噪音、按集聚合

4.2 完整剧本类:带详细场景描述

作品剧本数抓取来源Source 标识
Top Movies919 部电影imsdb.comimsdb
Top Movies171 部电影dailyscript.comdailyscript
Stargate SG-118 集imsdb.comimsdb
South Park129 集imsdb.comimsdb
Knight Rider80 集knightriderarchives.comknightriderarchives

这一类的特点是:原始材料是非结构化的纯文本剧本(含大量场景描述、动作指示),加工重点是编写解析器从 HTML/DOM 中提取对白、识别说话人、清理脏文本。两类来源在产出阶段最终都会落入完全相同的TEXT / METADATA / SOURCE三列格式。

五、工程实现(一):public.ipynb——结构化数据集的下载与聚合

public.ipynb 面向"数据本身已结构化"的五个来源,核心流程为:下载 → 读取为 DataFrame → 逐集聚合 → 统一三列 → 导出 Parquet

5.1 环境准备与依赖

仓库提供了 requirements.txt,内容如下:

beautifulsoup4 kaggle numpy pandas pyarrow requests tqdm

在 Colab 中运行时,Notebook 顶部预留了设置脚本(git clone 仓库后进入本目录、pip install -r requirements.txt)。其中kaggle库用于下载 Kaggle 数据集,使用前需要先在账户页面获取kaggle.json凭据(Notebook 代码注释中已说明)。

5.2 Friends:TSV 读取 + 场景分隔 + 过滤噪音

Friends 数据来自 emorynlp/character-mining 项目的friends_transcripts.tsv(制表符分隔),读取后得到 67373 行、每行包含season_id / episode_id / scene_id / utterance_id / speaker / tokens / transcript的语料表,覆盖 S01~S10 全部 236 集。

关键加工步骤(对应 Notebook 代码):

friends["group"] = friends[["season_id", "episode_id"]].apply( lambda x: f"{x[0]}_{x[1]}", axis=1 )
  • 构造 group 键:把season_id + episode_id拼成s01_e01形式的分组键,作为"一集"的粒度;
  • 季/集与标题映射:Notebook 内硬编码了全 10 季的剧名表(episodes[season][episode] -> title),用于把结构化 ID 还原为可读剧名;
  • 逐集聚合与文本拼接:核心循环如下(行号对应 public.ipynb):
data = {"TEXT": [], "METADATA": [], "SOURCE": []} for name, group in tqdm(friends.groupby("group")): metadata = { "show": "Friends", "season": group["season_id"].values[0], "episode": group["episode_id"].values[0], "title": episodes[group["season_id"].values[0]][group["episode_id"].values[0]], } text, last_scene = f"Friends - {metadata['title']}\r\n\r\n", None group.sort_values(by=["scene_id", "utterance"], ascending=True, inplace=True) for index, row in group.iterrows(): if last_scene is None: last_scene = row["scene_id"] elif last_scene != row["scene_id"]: last_scene = row["scene_id"] text += "\r\n---------------------------------------\r\n\r\n" if row["speaker"] == "unknown" or row["tokens"] == "[]" or pd.isna(row["transcript"]): continue text += f"[{row['speaker'].strip()}] {row['transcript'].strip()}\r\n" data["TEXT"].append(text) data["METADATA"].append(json.dumps(metadata)) data["SOURCE"].append("friends/emorynlp") data = pd.DataFrame(data)

这段代码体现了整个数据集格式规范的三个核心约定:

  1. 文本头Friends - {title}作为该集的标题行,接下来空两行开始正文;
  2. 场景分隔符:检测到scene_id变化时插入---------------------------------------分隔线,对应 README 示例中的"场景切换"语义;
  3. 噪音过滤:说话人为unknown、tokens 为空[]、台词缺失(NaN)三种情况直接跳过,保证产出文本的干净度。

最终样本形如:

Friends - The Pilot [Monica Geller] There's nothing to tell! He's just some guy I work with! [Joey Tribbiani] C'mon, you're going out with the guy! ...

METADATA 为{"show": "Friends", "season": "s01", "episode": "e01", "title": "The Pilot"}的 JSON 字符串,SOURCE 为friends/emorynlp

5.3 The Office / Marvel / Doctor Who:Kaggle 数据集下载与字段映射

这三个来源统一走 Kaggle API:

kaggle.api.dataset_download_files("nasirkhalid24/the-office-us-complete-dialoguetranscript", "office", unzip=True) kaggle.api.dataset_download_files("pdunton/marvel-cinematic-universe-dialogue", "marvel", unzip=True) kaggle.api.dataset_download_files("jeanmidev/doctor-who", "drwho", unzip=True)
  • The Office:读取office/The-Office-Lines-V4.csv(54626 行,字段season / episode / title / scene / speaker / line),去掉多余的Unnamed: 6列,同样用season_episode拼 group 键,按s01/e01的零填充格式写入 METADATA(f"s{str(x).zfill(2)}"),最终 186 集;
  • Marvel:18 部电影的对话(台词行含(Rhodes is telling the same story...)这类括号场景说明,保留在文本中),METADATA 含电影标题等信息;
  • Doctor Who:按episodeid分组(306 集),METADATA 额外记录季、集、集名,且文本头会带上播出方式和博士代数的信息(Doctor Who ({diffusion}; {doctors}) - {title}),体现"元信息融入正文"的灵活性。

三者的共同收尾动作:

data.to_parquet("office.pq", row_group_size=100, engine="pyarrow", index=False)

即以 100 行为一个 row group、使用 pyarrow 引擎导出 Parquet——这也是 README 数据卡片中download_sizedataset_size体积差异的来源之一(Parquet 的列式压缩特性)。

5.4 Star Trek:JSON 抓取与多剧集合

Star Trek 部分通过requests.get直接抓取all_scripts_raw.json(708 集,覆盖 DS9、ENT 等子系列),逐条解析为TEXT / METADATA / SOURCE三列(public.ipynb),METADATA 记录{"show": "Star Trek", "season": "DS9", "episode": ...}等字段,最后导出picard.pq

六、工程实现(二):imsdb.ipynb——非结构化剧本的爬虫解析

imsdb.ipynb 面向 imsdb.com 等纯文本剧本网站,数据没有结构化字段,必须先写解析器。Notebook 定义了IMSDbCrawler类,核心方法如下:

方法职责
_get(url, allow_unicode_errors)带容错地请求 HTML 页面
get_catalog()从 imsdb.com 抓取剧本目录(剧名 + 链接),导出 CSV 状态文件
download(url)下载单个剧本页面
_clean_dom(html)清洗 HTML DOM,去掉脚本、样式等无关节点
is_person(speaker)判断一行文本是否为说话人标注(用于区分台词与场景描述)
parse(html)解析剧本正文,提取[说话人] 台词结构
save(url)组合 download + parse,成功则写入本地.txt文件

6.1 清洗与说话人识别

_clean_dom负责把网页还原为接近纯文本的剧本:剥离<script><style>等节点后,通过正则与启发式规则判断"哪些行是[说话人]开头"。is_person从源码结构看,是后续parse阶段区分说话人行与场景描述行的关键判定函数。

6.2 剧本解析与脏数据过滤

parse阶段的处理要点(对应 imsdb.ipynb):

  • 对 HTML 实体做反转义(如&amp;&);
  • 统一换行符:\r\n\n归一化,多个连续空行压缩为两个;
  • 质量门控:若解析结果中没有出现连续的[说话人] 台词交替模式(正则\[.+?\] .+?\r\n\r\n\[.+?\] .+?\r\n\r\n),说明该页不是有效剧本,直接返回空串丢弃;
  • 画外音等特殊标注:文本内保留了[Person (v.o)]等括号注记,作为场景信息的一部分;
  • save方法额外以"解析后长度小于 128 字符"作为阈值,过滤掉过短的无效页面。

6.3 全量抓取与断点续爬

Notebook 主体展示了带断点续爬能力的批处理循环(imsdb.ipynb):

catalog = pd.read_csv(STATUS) crawled = catalog.copy() for index, row in catalog.iterrows(): if pd.isna(row["status"]): t = time.time() print(f"{row['alpha']} {row['title']}", end=" ") if ic.save(row["link"]): print("✔️", end=" ") crawled.at[index, "status"] = 1.0 else: print("❌", end=" ") crawled.at[index, "status"] = 0.0 print(f"- {(time.time() - t):.3f}s") crawled.to_csv(STATUS, index=False) if pd.notna(crawled["status"]).sum() % 25 == 0: print( f"▶▶▶ {pd.notna(crawled['status']).sum()} done " f"({int(crawled['status'].sum())} successful) out of {len(crawled)} ◀◀◀" ) print("Done.")

设计要点:

  • 状态落盘:每抓一部就写回 CSV 状态文件,中断后重跑时通过pd.isna(row["status"])自动跳过已完成条目;
  • 进度反馈:逐条打印耗时(秒),每完成 25 条打印一次累计统计(成功数 / 总数);
  • 结果保存save方法把剧本以{剧名}.txt写入FOLDER目录(文件名由 URL 清洗得到,非字母数字字符替换为-/_)。

这份脚本同时覆盖了 README 中"Top Movies (imsdb / dailyscript)、Stargate SG-1、South Park、Knight Rider"等来源的抓取基础设施。全部文本抓取完成后,再按与 public.ipynb 相同的三列格式聚合、导出,汇入最终数据集。

七、在 OpenAssistant 中的使用方式与复现路径

7.1 本地复现

# 进入数据集目录(仓库根目录相对路径) cd data/datasets/tv_dialogue pip install -r requirements.txt

随后按需执行两个 Notebook:先运行 public.ipynb 处理五个结构化来源(需要 Kaggle API 凭据kaggle.json),再运行 imsdb.ipynb 抓取剧本网站(耗时长,依赖断点续爬机制)。

7.2 数据加载示例

数据集以 HuggingFace Datasets 的dataset_info格式描述,训练侧可参考仓库的 HuggingFace 数据集导出工具 backend/oasst_backend/utils/hugging_face.py 与 backend/export.py 了解 OpenAssistant 如何将这类对话语料转换为 HuggingFace 格式;模型训练侧的对话数据加载可参考 model/model_training/custom_datasets/oasst_dataset.py。

7.3 注意事项

  • 来源授权:数据集以 MIT 协议发布,但各原始剧本来源的版权情况请以各来源网站为准,使用时注意区分"公开可抓取"与"可自由商用";
  • 语言范围:当前仅英语(language: en),如需多语言需自行扩展数据源;
  • 文本噪声:部分剧本源(尤其完整剧本类)仍可能残留格式噪声,训练前可结合数据清洗脚本进一步过滤。

八、总结

tv_dialogue 是 OpenAssistant 中一个典型的"多来源、单格式"数据集工程:5 个结构化来源 + 4 类非结构化剧本来源,最终统一为TEXT / METADATA / SOURCE三列的 2781 行语料。其工程价值不止于数据本身,更在于沉淀了一套可复用的方法论——结构化数据用 pandas 聚合、非结构化数据用爬虫 + 正则解析、全程以 Parquet 落地、元数据以 JSON 字符串随行存储、来源标识保证可溯源。这套"下载 → 清洗 → 统一格式化 → 导出"的流水线,可以直接迁移到任何其他影视对话语料的构建任务中。

如需继续深入,推荐阅读同一目录下的两个 Notebook 原文,以及 data/datasets 下其他数据集的 README,了解 OpenAssistant 对不同语料来源的差异化处理策略。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询