本文用一个纯本地的 Python 小脚本,把已经保存到本地的评论数据做一次完整整理:读取 JSON、清洗文本、统计点赞与回复、抽取高频词,最后导出 CSV 明细和一份文字摘要。脚本不发起任何网络请求,只处理磁盘上已有的数据文件,适合做内容复盘、选题分析和运营周报。
一、先说数据来源
评论数据的获取环节请走正规渠道:平台开放接口、创作者后台的导出功能,或者人工整理。拿到数据之后落成本地文件,再交给下面的脚本处理,这样职责是分开的:采集归采集,处理归处理。脚本里没有登录、签名、翻页请求之类的逻辑,这也是使用任何第三方工具时值得留意的一点。
二、输入文件的字段约定
脚本默认读取同目录下的 comments.json,支持两种结构:
- 顶层直接是数组;
- 顶层是对象,评论列表放在
comments字段里。
每条评论会用到这些字段:cid、text、nickname、like_count、reply_count、create_time、video_id。缺失字段会自动补默认值,如果你的导出文件字段名不一样,改 load_comments 里的取值即可,不需要动其他部分。
三、完整代码
# -*- coding: utf-8 -*-
"""
抖音评论数据整理工具(本地数据处理示例)
用途:
读取已经保存到本地的评论数据 comments.json,做清洗与统计,
导出可直接用表格软件打开的 CSV 明细,以及一份文字版摘要。
说明:
本示例只读取本地文件,不包含网络请求、登录、签名、翻页等采集逻辑。
数据来源请使用平台开放接口或官方导出功能,并遵守相应服务条款。
"""
import csv
import json
import os
import re
from collections import Counter
INPUT_FILE = "comments.json"
CSV_OUTPUT = "comments_clean.csv"
SUMMARY_OUTPUT = "summary.txt"
TOP_N = 10
# ---------- 步骤 0:准备示例输入,保证脚本可以直接运行 ----------
DEMO_COMMENTS = [
{
"cid": "1001",
"text": "这个教程讲得真清楚,收藏了![赞]",
"nickname": "阿强",
"like_count": 128,
"reply_count": 3,
"create_time": "2024-05-01 10:20:31",
"video_id": "7300000000000000001",
},
{
"cid": "1002",
"text": "请问背景音乐叫什么名字?https://example.com/a",
"nickname": "小满",
"like_count": 76,
"reply_count": 12,
"create_time": "2024-05-01 11:02:05",
"video_id": "7300000000000000001",
},
{
"cid": "1003",
"text": "已经跟着做了一遍,效果不错[比心]",
"nickname": "老李",
"like_count": 54,
"reply_count": 1,
"create_time": "2024-05-01 12:41:17",
"video_id": "7300000000000000001",
},
{
"cid": "1004",
"text": "第三步那里有点卡住了,能不能再讲细一点",
"nickname": "Nina",
"like_count": 31,
"reply_count": 5,
"create_time": "2024-05-02 09:15:44",
"video_id": "7300000000000000002",
},
{
"cid": "1005",
"text": "@老李 我也卡在第三步,后来把参数调小就好了",
"nickname": "阿强",
"like_count": 22,
"reply_count": 2,
"create_time": "2024-05-02 09:30:10",
"video_id": "7300000000000000002",
},
{
"cid": "1006",
"text": "哈哈哈哈这个演示太真实了",
"nickname": "小满",
"like_count": 18,
"reply_count": 0,
"create_time": "2024-05-02 10:05:59",
"video_id": "7300000000000000002",
},
]
def ensure_input_file():
"""没有输入文件时写一份示例数据,方便第一次运行。"""
if os.path.exists(INPUT_FILE):
return False
with open(INPUT_FILE, "w", encoding="utf-8") as f:
json.dump(DEMO_COMMENTS, f, ensure_ascii=False, indent=2)
return True
# ---------- 步骤 1:读取并规范化字段 ----------
def load_comments(path):
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
# 兼容两种结构:顶层数组,或 {"comments": [...]}
if isinstance(data, dict):
data = data.get("comments", [])
records = []
for item in data:
records.append({
"cid": str(item.get("cid", "")),
"text": str(item.get("text", "")),
"nickname": str(item.get("nickname", "")),
"like_count": int(item.get("like_count", 0) or 0),
"reply_count": int(item.get("reply_count", 0) or 0),
"create_time": str(item.get("create_time", "")),
"video_id": str(item.get("video_id", "")),
})
return records
# ---------- 步骤 2:文本清洗 ----------
URL_RE = re.compile(r"https?://\S+|www\.\S+")
AT_RE = re.compile(r"@[^\s@]+")
BRACKET_RE = re.compile(r"\[[^\[\]]{1,8}\]")
EMOJI_RE = re.compile(
"[\U0001F300-\U0001FAFF\U00002600-\U000027BF\U0001F1E6-\U0001F1FF]"
)
SPACE_RE = re.compile(r"\s+")
def clean_text(text):
"""去掉链接、@、表情占位符和多余空白,保留正文。"""
text = URL_RE.sub(" ", text)
text = AT_RE.sub(" ", text)
text = BRACKET_RE.sub(" ", text)
text = EMOJI_RE.sub(" ", text)
text = SPACE_RE.sub(" ", text)
return text.strip()
# ---------- 步骤 3:分词(有 jieba 就用,没有就退化处理) ----------
try:
import jieba
def tokenize(text):
return [w.strip() for w in jieba.lcut(text) if len(w.strip()) > 1]
except ImportError:
def tokenize(text):
"""没有 jieba 时用双字滑窗 + 英文单词,做粗粒度词频统计。"""
tokens = []
for seg in re.findall(r"[\u4e00-\u9fa5]+", text):
if len(seg) < 2:
continue
tokens.extend(seg[i:i + 2] for i in range(len(seg) - 1))
tokens.extend(re.findall(r"[A-Za-z]{2,}", text))
return tokens
STOPWORDS = {
"这个", "那个", "什么", "怎么", "可以", "就是", "真的", "感觉",
"我们", "你们", "他们", "自己", "一下", "一个", "还是", "已经",
"哈哈", "哈哈哈", "the", "and",
}
def keyword_counter(records, top_n=TOP_N):
counter = Counter()
for r in records:
text = r.get("clean_text") or r["text"]
for word in tokenize(text):
if word in STOPWORDS:
continue
counter[word] += 1
return counter.most_common(top_n)
# ---------- 步骤 4:汇总统计 ----------
def summarize(records):
total = len(records)
total_like = sum(r["like_count"] for r in records)
total_reply = sum(r["reply_count"] for r in records)
avg_like = round(total_like / total, 2) if total else 0
hot = sorted(records, key=lambda r: r["like_count"], reverse=True)[:TOP_N]
lines = [
"评论总数: %d" % total,
"点赞总数: %d" % total_like,
"回复总数: %d" % total_reply,
"平均点赞: %s" % avg_like,
"",
"高频词 Top %d:" % TOP_N,
]
lines += [" %s x%d" % (w, c) for w, c in keyword_counter(records)]
lines += ["", "点赞最高评论 Top %d:" % TOP_N]
for r in hot:
lines.append(" [%d赞] %s: %s" % (r["like_count"], r["nickname"], r["text"]))
return "\n".join(lines)
# ---------- 步骤 5:导出 CSV ----------
def export_csv(records, path):
fields = [
"cid", "nickname", "text", "clean_text",
"like_count", "reply_count", "create_time", "video_id",
]
with open(path, "w", encoding="utf-8-sig", newline="") as f:
writer = csv.DictWriter(f, fieldnames=fields)
writer.writeheader()
for r in records:
row = dict(r)
row["clean_text"] = clean_text(r["text"])
writer.writerow({
k: row.get(k, "") for k in fields})
def main():
if ensure_input_file():
print("未找到 %s,已生成示例数据。" % INPUT_FILE)
records = load_comments(INPUT_FILE)
print("读取评论 %d 条" % len(records))
# 先统一算好清洗结果,后面的统计和导出都复用
for r in records:
r["clean_text"] = clean_text(r["text"])
export_csv(records, CSV_OUTPUT)
print("已导出明细: %s" % CSV_OUTPUT)
report = summarize(records)
with open(SUMMARY_OUTPUT, "w", encoding="utf-8") as f:
f.write(report + "\n")
print("已导出统计: %s" % SUMMARY_OUTPUT)
print("-" * 40)
print(report)
if __name__ == "__main__":
main()
四、运行与输出
把文件保存为 douyin_comment_cleaner.py,然后执行:
python douyin_comment_cleaner.py
脚本会依次做四件事:
- 检查
comments.json是否存在,不存在就写一份示例数据,保证第一次运行就有结果; - 读取并规范化每条评论的字段;
- 清洗文本后导出
comments_clean.csv,编码用utf-8-sig,Excel 直接双击打开不会乱码; - 生成
summary.txt,包含评论总数、点赞总数、回复总数、平均点赞、高频词 Top 10,以及点赞最高的若干条评论。
控制台也会把这份摘要打印一遍,方便快速看一眼结果。
五、几个常见的改法
换输入输出路径。 改文件顶部的 INPUT_FILE、CSV_OUTPUT、SUMMARY_OUTPUT 三个常量即可,比如改成按日期命名的文件,方便多次运行保留历史。
改 Top 数量。 调整 TOP_N,高频词和热门评论的条数会一起变。
过滤噪声词。 中文没有天然的空格分词,STOPWORDS 里先把「这个」「就是」这类高频虚词挡掉,剩下的词才有参考价值。跑完一轮看到新的噪声词,直接往里加就行。
只看某段时间。 在 load_comments 之后加一个过滤,比如保留 create_time >= "2024-05-01" and create_time < "2024-05-08" 的记录。因为时间是 YYYY-MM-DD HH:MM:SS 这种定长字符串,直接比较大小就是按时间排序。
让词频更准。 执行 pip install jieba。脚本检测到 jieba 会自动切换成真实分词;没装的时候用双字滑窗兜底,结果会偏碎,但做粗筛够用。
按视频分组。 用 itertools.groupby 或一个字典按 video_id 聚合,就能比较同一账号下不同视频的评论热度差异。
六、还能继续做什么
清洗后的 clean_text 可以直接接情感分析,算出正负评价的比例;也可以按昵称统计活跃评论者,看看谁是铁粉;如果评论里带有 @ 关系,还能拉出一张简单的互动网络。数据量再大一些时,把列表换成 pandas 或者写进 SQLite 查询会省事很多。
最后提醒一句:评论数据会涉及个人信息,内部复盘用就够了,不要对外二次分发,也不要拿去训练会公开发布的模型。