

# 为内容分析研究准备 TikTok 字幕数据

可以。你亲自核对公开的 TikTok 字幕之后，就可以采集它们，用于定量内容分析。定量内容分析是指：在你划定的一组视频里，对一条清楚的内容规则做计数。字幕轨是已经附在视频上的公开文本。在本文中，字幕稿就是这段字幕文本。它不是对每一句口播的重新抄录。核对是指你自己把字幕和视频对照。返回的文本是字幕数据。它不是语音、画面或每一条 TikTok 帖子的完整记录。

一个公开的研究问题询问如何为一项研究采集 TikTok 字幕稿。它也询问如何检查准确性，以及如何在 R 中分析这些文本。R 是用来处理表格和计数的程序。这个问题表明存在一种需求。它并不证明这种方法有效。它也没有说明有多少研究需要它。见[关于 TikTok 字幕稿的公开问题](https://www.reddit.com/r/CompSocial/comments/1h1zijh/help_needed_scraping_tiktok_video_transcripts_for/)。

你用一个 Actor 采集数据行。Actor 是托管的数据采集程序。托管是指它作为服务运行，而不是作为你电脑上的一个文件。这个 Actor 是 [TikTok Transcript Scraper](https://apify.com/chronometrica/tiktok-transcript-scraper)。Task 是已保存的输入预设。预设是存储好的一组输入选择。当前 Task 是 [TikTok Transcript Dataset for Content Analysis](https://apify.com/chronometrica/tiktok-transcript-scraper/examples/tiktok-transcript-content-analysis-dataset)。Actor 采集来源行。编码和计数由你来做。一个公开 Task 只是起始输入。它并不表示分析会自行运行。

## 当前采集程序返回的内容

这些公开事实已在 2026-10-13 核对。当前版本是 0.1.36。每次运行在 `startUrls` 中接受 1 到 100 个已知的公开目标。目标可以是视频 URL、短链接或数字视频 ID。这个 Actor 不搜索个人主页、关键词或声音页面。你必须已经知道这些视频。

`preferredLanguages` 是公共字幕语言的有序列表。Actor 选取第一条匹配的公共字幕轨。空列表使用第一条公共字幕轨。它不翻译文本。它不为没有字幕的声音生成文本。它不采集评论。它不分配内容编码。

已保存的 Task 输入是：

```json
{"startUrls": ["https://www.tiktok.com/@hankgreen1/video/7047596209028074758"], "preferredLanguages": []}
```

当前采集程序返回的内容

把该示例换成你自己已知的公开视频。做单视频试点时，在 `startUrls` 中放入一个 URL。如果英语是你的研究语言，把 `preferredLanguages` 设为 `["en"]`。如果研究使用另一种语言，写下该语言规则并使用它的代码。不要把 `["en"]` 复制到另一种语言的研究中。空列表可能返回某条其他公共字幕轨。

一个数据集行对应每一个提交的输入。失败的输入仍然有一行。保留该行。它是这次尝试的证据。这些字段名称是精确的：`inputIndex`、`inputUrl`、`videoId`、`videoUrl`、`authorHandle`、`durationSeconds`、`languageCode`、`text`、`segments`、`status`、`statusReason`、`isBillable` 和 `scrapedAt`。每个片段有 `startSeconds`、`endSeconds` 和 `text`。TikTok 时间使用这两个键。按返回的原样使用 `startSeconds` 和 `endSeconds`。把每个 `videoId` 当作文本保留，不要当作数字。如果程序把长 ID 存成数字，这个 ID 可能会改变。

`status` 可以是 `succeeded`、`no_public_captions`、`language_unavailable`、`duplicate_input`、`not_tiktok`、`invalid_input`、`unsupported_url`、`video_unavailable`、`not_public` 或 `blocked_or_challenged`。还可能出现提取失败和字幕失败。成功要求文本不是空的。失败状态下的 null 或空字幕稿并不是口播里的静音。视频里仍可能有语音。你只知道没有返回可用字幕。

`isBillable` 仅在原生字幕稿结果成功时为 true。字段名中的 b 是小写。其他行作为证据留在数据中。`isBillable` 不描述整次运行。一次运行可以同时有 true 行和 false 行。不要把整次运行称为免费。

运行账使用这些精确名称：`inputsReceived`、`resultRows`、`billableTranscriptRows`、`nonBillableResultRows`、`duplicateVideosSkipped` 和 `statusCounts`。这份运行账用来核对这次运行。它并不表明你的视频代表所有创作者。

在 2026-10-13 用已保存的示例做了一次单视频检查。该行成功。视频 ID 是所请求的 ID，`7047596209028074758`。语言代码是 `eng-US`。像 `eng-US` 这样的代码是地区字幕标签。它表示带有美国地区标记的英语。它不是翻译。文本不是空的。该行有 12 个片段。片段时间具有 `startSeconds` 和 `endSeconds` 这种形状。这里不重复该视频的字幕词。这次检查证明那一个来源上存在公开字幕路径。它不证明词语完全正确。它不证明每条视频都会成功。它不证明你的内容编码有效。你自己的试点仍然是必需的。

## 八项操作

1. 界定问题、编码和样本

写下一个研究问题。写下一条你可以计数的内容编码。内容编码是你标为存在或不存在的特征。按一条你能够陈述的规则选择已知视频 URL。在采集之前写明时间范围、语言和创作者范围。

抽样台账是你选定的每一条视频的清单。在查看字幕之前填好这份台账。不要只因为存在字幕就选择视频。如果你只保留有字幕的视频，样本就已经变了。没有字幕的视频会消失，而不会显示为缺失。

1. 声明计数单位

选择你要计数的单位。单位可以是一条视频、一个字幕片段，或你定义的一段文字。在这些步骤里，计数单位是视频。片段是带时间的一段字幕文本。片段是该视频的证据。它们不是额外的视频。

三个片段即使编码相同，仍然算一条视频。在运行之前写下这条规则。保留输入 URL、返回的 `videoId` 和你自己的 `selected_video_key`。研究键是你的。即使 URL 形式改变，它也保持不变。

1. 测试一条选定视频

在任何批量运行之前，先运行一条选定视频。这是试点。试点是对整条路径的小型测试。检查 `status`、`videoId`、`languageCode` 和 `text`。成功时文本必须非空。如果研究语言是英语，在这次试点中把 `preferredLanguages` 设为 `["en"]`。

然后[把一段短文本和音频对照](/zh-Hans/articles/%E7%A0%94%E7%A9%B6%E5%89%8D%E6%A0%B8%E5%AF%B9tiktok%E5%AD%97%E5%B9%95%E7%94%A8%E8%AF%8D/)。同时看屏幕上的画面。记下漏掉的词、多出的词和错误的词。成功的一行并不能证明词语准确。上面的 12 个片段检查只说明公开路径可以返回字幕。那不是你的核对。

1. 运行有界列表并核对

一次运行只发送你已知的 URL，最多 100 个。更长的选定列表需要另一次运行。把每一个提交的输入对应到一行结果。使用 `inputIndex`、`inputUrl` 和 `videoId`。把这些行与运行账核对。

检查 `inputsReceived`、`resultRows`、`billableTranscriptRows`、`nonBillableResultRows`、`duplicateVideosSkipped` 和 `statusCounts`。把重复项留在笔记中，并将该视频只计数一次。把缺失字幕留在台账中，但放在成功集合之外。来源访问失败也同样处理。这些状态包括 `video_unavailable`、`not_public`、`blocked_or_challenged`、`unsupported_url`、`not_tiktok` 和 `invalid_input`。

切勿把失败的空文本编码为“编码不存在”。空文本表示你没有观察到该编码。它并不表示该编码不存在。

1. 制作视频表和片段表

为每条选定视频做一行。使用这些列：`selected_video_key`、`videoId`、`videoUrl`、`languageCode`、`caption_status`、`caption_validated`、`study_eligible` 和 `exclusion_reason`。`study_eligible` 记录该视频是否可以进入计数。它不是内容编码。`caption_validated` 记录你是否对照视频核对了字幕。

无法核对字幕时使用 `unknown`。真实状态是 unknown 时，不要把 `caption_validated` 写成 no。失败的一行没有经过核对。未完成的复查也没有经过核对。

片段放在第二张表。使用视频键、`startSeconds`、`endSeconds`、`text` 和一项来源观察。把片段数组放进一个电子表格单元格是糟糕的导出。CSV 是扁平的文本表。嵌套列表不能安全地放进这种扁平形状。先导出 JSON。JSON 是可以在一个项目内保留列表的文本格式。然后有意地展平。展平是指为每个片段做成表格中的一行。在每个片段行上重复视频键。不要按逗号拆分文本。句子里含有逗号。保持原始字幕文本不变。把每个编码放进新列。

1. 写一条简单的编码规则，并用虚构例子测试这条规则

从这一步起的例子都是虚构的。它们不是 Actor 的输出。不要把这些 ID 粘贴到真实输入中。它们的视频 URL 是未知的。

写一条简短规则，并给它一个版本。对于简单的整词精确规则，要定义词、字母大小写和词边界。词边界避免一个短词匹配到更长词的内部。含义含糊的用法要自己复查。关键词规则只找出该模式。它不证明说话人的意思。只靠画面表达的要点可以和词语不一致。讽刺也可以和词语不一致。这些情况需要人来编码。

这里的虚构编码是明确要求量水的指令。规则版本 v1 在已核对的字幕文本给出该指令时标记编码 1。整词“measure”在任何字母大小写下都计数。词“measurement”是另一个词。要复查它。不要只靠关键词规则标记它。只有已核对、且没有该指令的字幕才可以标编码 0。否则编码保持 unknown。

1. 检查一个试点集合，然后在 R 中导入

在大型研究之前，先给一个小试点集合编码。可以由两个人各自独立编码。也可以由你稍后再次编码并比较。保存 `code`、`code_version`、`coder`、`decision_note` 和时间戳。时间戳记录做出决定的时间。原始文本放在单独的列中。

R 可以读取扁平的 CSV。告诉 R：ID 列是文本。不要让 R 把 ID 猜成数字。下面的代码片段只用 R 自带功能。它使用虚构行。它不添加程序包。它不是经过检验的研究标准。这里 `denom` 是分母。分母是你用来做除法的合格视频数量。`NA` 表示不可用。本例用 `NA` 表示未知编码。

```r
videos <- data.frame(
  videoId = c("video-v1", "video-v2", "video-v3", "video-v4", "video-v5"),
  study_eligible = c("yes", "yes", "no", "no", "no"),
  code = c(1, 0, NA, NA, NA),
  stringsAsFactors = FALSE
)
stopifnot(is.character(videos[["videoId"]]))
eligible <- videos[["study_eligible"]] == "yes"
stopifnot(!any(is.na(videos[["code"]][eligible])))
code_count <- sum(videos[["code"]][eligible])
denom <- sum(eligible)
share <- code_count / denom
```

八项操作

`code_count` 是 1。`denom` 是 2。`share` 是 0.5。这些 `NA` 是未知编码。它们不是零。`stopifnot` 那一行会在合格视频带有未知编码时停止脚本。

导出真实 CSV 时，保留同样的文本检查：

```r
videos_from_csv <- read.csv(
  "video_table.csv",
  stringsAsFactors = FALSE,
  colClasses = c(videoId = "character")
)
stopifnot(is.character(videos_from_csv[["videoId"]]))
```

八项操作

你也可以在 R 自带功能中测试这条虚构的词规则：

```r
raw_text <- "I measure the water before I pour it"
hit <- grepl("\\bmeasure\\b", raw_text, ignore.case = TRUE)
ambiguous <- grepl("\\bmeasure\\b", "measurement", ignore.case = TRUE)
```

八项操作

`hit` 是 TRUE。`ambiguous` 是 FALSE。第二个结果说明：更长的词需要人工复查规则，而不能静默匹配。

1. 连同完整结果台账报告计数

计算该编码所占比例时，只用合格视频作分母。合格是指 `succeeded`、文本非空，并且 `caption_validated` 为 yes。在该比例旁边报告每一条选定视频的结果。读者必须能看到失败行和未完成行。把发现限制在你选定并观察到的视频。不要把它说成所有创作者的结果。不要把它说成全部政治言论的结果。

缺失字幕可能改变结论。本文停在台账。它不讲授[这些缺失行能使结果改变多少](/zh-Hans/articles/%E7%BC%BA%E5%A4%B1tiktok%E5%AD%97%E5%B9%95%E5%A6%82%E4%BD%95%E6%94%B9%E5%8F%98%E7%A0%94%E7%A9%B6%E6%A0%B7%E6%9C%AC/)。

## 五条虚构视频的计数

同样的虚构在这里继续。V1 到 V5 是编造的研究标签。`videoId` 的值是编造的文本标签。它们不是真实的 TikTok ID。每一行的 `videoUrl` 都是未知的。不要把这些标签发送给 Actor。真实的 `languageCode` 可以是地区值，例如 `eng-US`。这份虚构使用 `en`，因为声明的研究语言是英语。在表中，unknown 是研究用的值。在 R 例子中，`NA` 存储同一个未知。不要把 unknown 存成 0。

声明的编码仍然是明确要求量水的指令。

V1 是英语。它成功了。字幕已核对。文本是“I measure the water before I pour it”。这段文本是明确要求量水的指令。编码是 1。

V2 是英语。它成功了。字幕已核对。文本是“I pour water into the tray”。它让人去倒。它没有让人去量。编码是 0。

V3 的状态是 `no_public_captions`。文本是空的。`languageCode` 是 unknown。不要编造一个。`caption_validated` 是 unknown，不是 no。编码是 unknown。空文本不是编码 0。它也不是静音的证据。

V4 的状态是 `language_unavailable`。文本是空的。`languageCode` 是 unknown。`caption_validated` 是 unknown。编码是 unknown。这次失败和 V3 的失败不是同一种。把两个原因分开保留。

V5 是英语。它成功了。有人把字幕和音频做了对照。字幕漏掉了测量用语。核对没有完成。`caption_validated` 是 unknown，不是 no。`study_eligible` 是 no。编码保持 unknown。成功不等于可信的字幕。不要把这一行改成编码 0。

只有当 `study_eligible` 是 yes 时，视频才进入比例。这个决定和编码是分开的。V1 和 V2 合格，因为字幕成功、文本非空，并且有人核对过。V3 和 V4 不合格，因为采集失败。V5 不合格，因为核对没有完成。V1、V2 和 V5 的 `isBillable` 会是 true，因为那些字幕稿结果成功了。V3 和 V4 的 `isBillable` 会是 false。V5 可计费，但仍然不合格。计费标志不决定研究计数。

视频表：

| selected_video_key | videoId | videoUrl | languageCode | caption_status | caption_validated | study_eligible | exclusion_reason |
| --- | --- | --- | --- | --- | --- | --- | --- |
| V1 | video-v1 | unknown | en | succeeded | yes | yes | none |
| V2 | video-v2 | unknown | en | succeeded | yes | yes | none |
| V3 | video-v3 | unknown | unknown | no_public_captions | unknown | no | 没有公开字幕；空文本未编码 |
| V4 | video-v4 | unknown | unknown | language_unavailable | unknown | no | 请求的语言缺失；空文本未编码 |
| V5 | video-v5 | unknown | en | succeeded | unknown | no | 音频核对未完成；漏掉了测量用语 |

编码表：

| selected_video_key | code | code_version | coder | decision_note | timestamp |
| --- | --- | --- | --- | --- | --- |
| V1 | 1 | v1 | coder-a | 明确要求量水的指令 | example-only |
| V2 | 0 | v1 | coder-a | 只有倾倒指令；没有量取指令 | example-only |
| V3 | unknown | v1 | coder-a | 字幕失败；未观察到编码 | example-only |
| V4 | unknown | v1 | coder-a | 字幕失败；未观察到编码 | example-only |
| V5 | unknown | v1 | coder-a | 字幕未核对；未观察到编码 | example-only |

片段表：

| selected_video_key | startSeconds | endSeconds | text | source_observation |
| --- | --- | --- | --- | --- |
| V1 | 0.5 | 2.5 | I measure the water before I pour it | 已核对的字幕，作为编码 1 的证据 |
| V1 | unknown | unknown | unknown | 第二条测量片段；只是额外证据；此处未设定时间 |
| V2 | unknown | unknown | I pour water into the tray | 已核对的字幕，作为编码 0 的证据 |
| V5 | unknown | unknown | unknown | 字幕漏掉了一句口播的测量用语；此处未写入那些词 |

V3 和 V4 没有片段行。它们的结果文本是空的。不要添加一条 0 秒的片段。零会看起来像真实时间。缺失的时间仍是 unknown。

第一段 V1 片段在 2.5 秒结束，在 0.5 秒开始。2.5 减 0.5 是 2.0 秒。这个长度使用 `endSeconds` 和 `startSeconds`。第二段 V1 片段也支持测量编码。它不会变成第二条视频。V1 仍然是一条已编码的视频。计数单位是视频。

已核对且合格的视频：2。编码为 1 的视频：1。1 / 2 = 0.5，即 50%。选定台账仍有 5 条视频。1 / 5 = 0.2，即 20%。不要报告 20%。V3、V4 和 V5 没有被核实为编码 0。未知不是零。

这五条选定视频的结果计数：2 条已核对且合格，2 条失败，1 条核对未完成。两次失败的状态不同。一个是 `no_public_captions`。另一个是 `language_unavailable`。

假设你把这五个输入提交了一次。假设没有重复。下面预期的运行账是根据这份虚构做的算术。它不是真实回执。`inputsReceived` 会是 5。`resultRows` 会是 5。`billableTranscriptRows` 会是 3。`nonBillableResultRows` 会是 2。`duplicateVideosSkipped` 会是 0。`statusCounts` 会显示 `succeeded` 3、`no_public_captions` 1 和 `language_unavailable` 1。你的真实运行必须对照你实际收到的运行账来检查。

这个例子说明额外片段不会增加视频。它也说明合格分母。它没有展示一组真实视频。它没有展示字幕准确率。它没有展示超出这些编造行的效应。

已知 V1 片段的扁平 JSON 形状是：

```json
{
  "videoId": "video-v1",
  "segments": [
    {
      "startSeconds": 0.5,
      "endSeconds": 2.5,
      "text": "I measure the water before I pour it"
    }
  ]
}
```

五条虚构视频的计数

该对象是为展平步骤准备的虚构对象。展平之后，上面的片段表是编码副本。视频表仍然是计数副本。

## 下一步

在采集完整列表之前，先核验一条选定的公开视频。检查输入、状态行和编码台账。使用 [TikTok Transcript Scraper](https://apify.com/chronometrica/tiktok-transcript-scraper) Actor 和 [TikTok Transcript Dataset for Content Analysis](https://apify.com/chronometrica/tiktok-transcript-scraper/examples/tiktok-transcript-content-analysis-dataset) Task。把示例视频换成你自己已知的一条公开 URL。

## 来源和延伸阅读

- [TikTok Transcript Scraper](https://apify.com/chronometrica/tiktok-transcript-scraper)
- [TikTok Transcript Dataset for Content Analysis](https://apify.com/chronometrica/tiktok-transcript-scraper/examples/tiktok-transcript-content-analysis-dataset)
- [关于 TikTok 视频字幕稿的公开问题](https://www.reddit.com/r/CompSocial/comments/1h1zijh/help_needed_scraping_tiktok_video_transcripts_for/)