当团队为下一次营销活动寻找主题时,手动浏览几十个账号很快就会变成一堆缺乏统一方法的笔记。一位分析师记录播放量,另一位记录视频文案,而措辞相近的帖子却被放进不同的表格。一周后,很难判断反复出现的究竟是某个主题、呈现方式,还是仅仅一条表现出色的视频。

一种实用做法是维护一份规模不大的公开账号列表,定期以结构化形式获取其视频信息,并将每次采集保存为独立快照。随后,可以比较不同时间的帖子和可用指标,并用语言模型对文案进行初步归类。下面介绍一个基于 Python 和 jsonscraper 的 TikTok Scraper API 研究流程。
采集数据前先明确研究问题
假设某个小型运动营养品牌的营销团队想了解,选定的公开 TikTok 创作者经常发布哪些主题,以及哪些视频值得人工查看。目标是在观察期内比较预先选定账号的帖子,而不是建立一个通用的 TikTok 人气排行榜。
这也划定了分析范围。采集所选账号的动态,有助于研究这些账号发布的内容和频率。可用的公开计数指标可以在请求时记录。若要评估自有广告的效果,则还需要单独的数据,例如广告系统或网站分析工具中的支出和转化数据。
接入 API 前,先记录研究方案:账号列表、开始日期、采集频率、分析字段以及帖子纳入标准。记录每次请求的时间,便于区分单条视频指标的变化与两组观测结果之间的差异。
采集什么,以及如何衡量
小型项目需要三层数据。第一层是账号标识和采集启动时间。第二层是保存下来、供后续核验的 API 原始响应。第三层是便于查询和报告的规范化字段:视频 ID、文案、发布时间以及可用的互动计数。
应根据具体接口的样例来确认字段名称和嵌套结构。不要预先假定列表一定叫作 videos,或播放量字段一定叫作 views。先执行文档所述的请求并检查实际返回的 JSON,再设置转换逻辑,映射到自己的数据结构。
有用的指标包括某个时间段内新增帖子的数量、帖子主题分布,以及同一视频在不同快照中的公开计数变化。比较最后一项时,应匹配视频 ID。这样一来,已发布视频的动态变化就能与新帖子的指标区分开来。
接入 TikTok Scraper API
TikTok Scraper API 说明列出了基础地址 https://tiktok.evelode.com;路由参考文档确认了 GET /getUserFeed 以及参数 query、max_cursor、count、region、with_pinned_posts、sort_type 和 cache_timeout。首次请求只需在 query 中指定账号,并通过 count 等参数设置返回条目数。license_key 通过 query string 传递;API 参考文档也说明了如何配置该密钥。集成前,可以在 Postman 集合中核对接口并检查响应。
将密钥存放在环境变量中。不要把它写进源代码、代码库、表格或客户端应用。执行请求后,保存一份成功返回的 JSON 样例,并注明日期和路由名称,将其作为规范化适配器和测试的依据。
可直接使用的 Python 最小请求示例
参考文档指出,用户动态的响应包含外层状态和 tiktok 对象,帖子列表位于 aweme_list 中。记录中可能包含 ID、文案、发布时间、作者和统计数据。具体字段及取值应以实际响应为准:部分数据可能缺失。
import json
import os
from datetime import datetime, timezone
from pathlib import Path
import requests
API_KEY = os.environ["JSONSCRAPER_TIKTOK_LICENSE_KEY"]
ENDPOINT_URL = "https://tiktok.evelode.com/getUserFeed"
def fetch_user_feed(handle: str, count: int = 20) -> dict:
response = requests.get(
ENDPOINT_URL,
params={
"license_key": API_KEY,
"query": handle,
"count": count,
"region": "US",
},
timeout=45,
)
response.raise_for_status()
payload = response.json()
if not isinstance(payload, dict):
raise ValueError("预期得到 JSON 对象")
if payload.get("status") != "ok":
raise RuntimeError(f"API 返回状态:{payload.get('status')!r}")
tiktok = payload.get("tiktok")
if not isinstance(tiktok, dict):
raise ValueError("响应中缺少 tiktok 对象")
return payload
def save_raw(payload: dict, account: str, output_dir: str = "data/raw") -> Path:
collected_at = datetime.now(timezone.utc).isoformat()
safe_account = "".join(c for c in account if c.isalnum() or c in "-_@")
path = Path(output_dir) / f"{safe_account}_{collected_at.replace(':', '-')}.json"
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(
json.dumps(
{"collected_at": collected_at, "account": account, "payload": payload},
ensure_ascii=False,
indent=2,
),
encoding="utf-8",
)
return path
def normalize_items(payload: dict, account: str, collected_at: str) -> list:
items = payload["tiktok"].get("aweme_list", [])
if not isinstance(items, list):
raise ValueError("字段 tiktok.aweme_list 不是列表")
observations = []
for item in items:
if not isinstance(item, dict):
continue
stats = item.get("statistics")
stats = stats if isinstance(stats, dict) else {}
author = item.get("author")
author = author if isinstance(author, dict) else {}
observations.append({
"account_handle": account,
"collected_at": collected_at,
"video_id": item.get("aweme_id"),
"caption": item.get("desc"),
"published_at_epoch": item.get("create_time"),
"author_handle": author.get("unique_id"),
"views": stats.get("play_count"),
"likes": stats.get("digg_count"),
"comments": stats.get("comment_count"),
"shares": stats.get("share_count"),
})
return observations
if __name__ == "__main__":
account = "creator_handle"
payload = fetch_user_feed(account, count=20)
collected_at = datetime.now(timezone.utc).isoformat()
raw_path = save_raw(payload, account)
observations = normalize_items(payload, account, collected_at)
print(f"已保存:{raw_path};记录数:{len(observations)}")
运行前,在环境中设置 JSONSCRAPER_TIKTOK_LICENSE_KEY,并安装 requests 库。代码会检查 HTTP 响应、外层状态以及 tiktok 对象是否存在,随后读取 aweme_list。它会保存原始 JSON,并提取参考文档确认过的字段;缺失值则留空。返回的动态只是 API 本次返回的一组结果,因此报告应关联请求时间和所选账号。
保存快照并比较帖子
小型项目可以使用 SQLite 或 PostgreSQL。原始响应应与规范化记录分开保存。观测数据表可以采用以下结构:
CREATE TABLE IF NOT EXISTS video_observations (
account_handle TEXT NOT NULL,
video_id TEXT NOT NULL,
collected_at TEXT NOT NULL,
published_at_epoch INTEGER,
caption TEXT,
views INTEGER,
likes INTEGER,
comments INTEGER,
shares INTEGER,
raw_file TEXT,
PRIMARY KEY (account_handle, video_id, collected_at)
);
每次运行都新增一个快照,而不是覆盖旧数据。比较计数时,仅在同一视频的两个快照都存在相应数值的情况下计算差值。报告中应包含两次请求的间隔和观测时间,以说明指标变化是在多长时间内累积的。
进行主题分类时,可以先从小型类别词表入手,之后再按需添加分类器或语言模型。向模型提供清理后的文案、帖子 ID 和预先定义的类别。与自由形式的概述相比,包含类别和简短依据的结构化结果更便于在不同轮次之间比较。
加入带可核验标注的语言模型
模型可以先把文案归入“食谱”“训练”“产品评测”和“个人故事”等主题。开始时,人工标注几十条帖子。将自动标签与这些样例比较,调整类别定义,并在修改提示词或模型后重新评估。
新增帖子数量、计数指标的中位数以及快照之间的差值,都更适合用普通代码计算。模型则用于文本分类和生成简短说明。保留原始文案和数值观测,供后续核验。
同时保存帖子 ID、提示词版本、处理日期和所用模型。若分类有误,分析师便能找到对应文案并修正规则。建议将初始标签和人工修正分别存储。
自动化运行
手动运行并验证后,可将脚本迁移到 cron、GitHub Actions、n8n 或 Make。调度器按计划启动采集、传入账号列表、记录运行结果,并将报告发送到指定存储位置。将密钥保存在运行环境的机密配置中。先用一个账号验证流程,再逐步扩展列表。
将自动化拆分为几个阶段:账号队列、API 调用、保存原始响应、规范化、写入快照和生成报告。单个账号发生错误时,不应抹去其他账号的结果。可以通过运行 ID 和数据库唯一约束,让重复运行具备幂等性。
记录时间、账号、路由、HTTP 状态、运行时长、处理条目数和错误说明。不要将 API 密钥写入日志。cache_timeout 是 /getUserFeed 支持的参数;如果需要指定缓存时长,可以将其加入请求。API 参考文档将默认值说明为 3600 秒。
发布报告前检查数据质量
将几条规范化记录与原始 JSON 对照。检查重复采集时是否通过 ID 识别同一视频、日期解析是否符合预期、数值字段类型是否恰当,以及缺失数据的比例是否明确。然后再次运行,并确认数据库保留了旧观测且新增了新观测。
不要混淆不同账号和采集时段。比较主题时,应使用相同的时间段长度,或在报告中明确标注。分析计数指标的变化时,应记录两次快照间隔,让读者了解比较区间的时长。
保留原始文案,并将清理后的版本单独存储。表情符号、话题标签、链接和语言特征都可能影响分类;分别保存这些字段,有助于检查清理操作的影响,并在不重新请求 API 的情况下重新计算类别。
流程能带来什么结果
经过几个周期后,团队将得到帖子和快照表、按主题初步归类的文案,以及所选时段内新视频的报告。这有助于筛选值得专家审看的视频,发现所选账号反复讨论的主题,并检查不同观测日期之间公开计数指标的变化。
基于这些数据,可以为编辑日历提出假设:比较同一问题的不同呈现方式,或找出适合在自有内容中测试的主题。若要评估商业效果,则应将观测结果与内部营销活动数据进行对照。
要开始使用,可选择一小批公开账号,并在 Postman 请求集合中测试 GET /getUserFeed。只有路由文档和实际响应确认过的参数及字段路径,才应写入代码。
负责任地采集公开数据
根据研究目标限制账号列表,只采集必要字段,并设定原始响应的保留期限。将数据用于商业目的前,请检查适用的平台条款以及所在司法辖区的个人数据要求。设置访问控制并删除过期快照,有助于确保流程不超出任务范围。
向 AI 服务发送数据前,删除模型不需要的字段。在报告中注明帖子 ID 和观测日期,方便员工打开记录并核实结论依据。
启动简明清单
- 明确细分领域、账号列表、时间段和分析问题。
- 在参考文档中核对
GET /getUserFeed的参数,并在 Postman 中检查实际响应。 - 保存 JSON 样例,并针对已确认字段设置规范化程序。
- 保存原始响应,并单独记录带有采集时间的快照。
- 用代码计算数值指标;使用 AI 对文本进行初步归类。
- 添加运行计划、日志记录、重试处理和数据保留期限。
这样,监测流程就成为一项可复现的研究:每个结论都关联到具体帖子和观测时间,代码也以所选路由的实际结构为依据。