jsonscraper

如何发现 TikTok 趋势的早期信号:自动监控搜索结果

定期采集搜索结果快照,对比视频指标,并将变化转化为可验证的内容团队信号。

编辑政策 报告错误

当团队注意到 TikTok 上出现了新话题时,竞争对手往往已经在讨论它。原因通常很简单:搜索由人工不定期检查,找到的链接则留在聊天记录中,没有统一表格、采集日期或可比较的指标。要更早发现变化,可以将搜索变成可重复的流程:记录查询词,定期保存找到的视频,并只比较实际观察到的结果。

文章:如何发现 TikTok 趋势的早期信号:自动监控搜索结果
摘要:手动搜索 TikTok 不适合定期分析:结果变化很快,单个视频也容易被误认为趋势。本文介绍如何通过 TikTok Scraper API jsonscraper 构建每日搜索词监控,保存原始响应、规范化数据,并筛选主题供后续核查。
图片用途:封面——核心理念
章节:
视觉主题:A phy
© jsonscraper · AI 生成的插图

下面介绍如何使用 Python 和 TikTok Scraper API jsonscraper 构建这类监控。例如,代理机构可以追踪多个主题查询词,为发布日历提供参考;品牌也可以查看哪些与主题相关的公开视频获得了显著播放量和互动。API 会返回 TikTok 搜索结果,包括视频描述、作者、发布日期、链接和可用计数器。脚本将每次响应存入档案,并生成 CSV 快照供分析使用。

核心原则是,不要计算抽象的“趋势”,而要分析自己观察记录中的变化:哪些视频新进入样本,哪些已知视频的公开计数器发生了变化,哪些措辞在描述中反复出现。这样可以为内容团队提供一份可核查的主题候选清单,供人工评估,并保留每项结论的来源记录。

为什么手动搜索难以成为有效监控

搜索结果是不断变化的样本,并非稳定的发布目录。如果员工今天用某个词找到十条视频,下周又重新搜索,就很难比较结果:旧链接可能已经找不到,搜索结果集会发生变化,计数器也会累积新的数值。没有保存的快照,就无法还原团队在某一天具体看到的内容。

即使只是每天进行一次简单采集,也能解决其中一部分问题。每次运行都会保存观察日期、搜索词、视频 ID、作者、描述、链接和可用指标。下一次运行时,可以按视频 ID 匹配记录;如果两个版本都包含相应字段,还可以计算计数器数值的差异。

这些数值应被视为 API 响应中所见视频的公开指标,而不是 TikTok 的完整覆盖面或全面的热门程度排名。搜索结果可能变化,某些字段可能缺失,指标也取决于采集时点。因此,流水线的目标是生成可复现的工作样本并发现待核查对象,而不是根据一个数字就宣布某个主题普遍流行。

方案结构:搜索、归档、比较

这条流水线包含五个部分:关键词配置、API 请求、未经改动的 JSON 归档、规范化记录层,以及变化报告。这样的划分便于分别检查每个步骤。如果响应结构发生变化或个别字段缺失,仍可使用原始 JSON 重新处理数据。

  1. 确定查询词集合。选择与产品、行业或受众需求相关的具体短语。例如,厨电品牌可以追踪“meal prep”“air fryer recipes”以及某个具体产品类别的名称。请将查询词列表保存在配置文件中,而不是写死在处理逻辑里。
  2. 设定采集规则。选择采集时间安排,并在试运行阶段使用较少的结果数量。记录日期、地区和搜索参数。为了进行时间比较,重复使用相同设置比尝试在一次运行中获取尽可能宽泛的结果更重要。
  3. 保存原始响应。将 JSON 与运行时间和搜索词一同写入文件。归档有助于还原原始记录,也能在不重新调用 API 的情况下修正规范化流程。
  4. 规范化已确认的字段。只提取响应中实际存在的字段:ID、描述、发布时间、作者、链接和可用统计数值。缺失值应留空,不要替换为零。
  5. 比较快照。按 ID 匹配视频,并分别保存每次观察记录。随后即可找出归档中新增的发布内容,并计算观察到的指标数值差异。

初次测试时,选择两到三个查询词和较少的结果数量。检查描述和作者是否符合需求、实际返回了哪些字段,以及相同条件下的结果集重复程度。完成这些检查后,再考虑扩展词汇表并安排定时运行。

TikTok Scraper API 可以查询哪些内容

在 jsonscraper 官方文档中,路由 GET /searchVideo 用于按关键词搜索视频。必填参数为 keyword。文档列出的可选参数包括 count、cursor、date_posted、sort_by、region 和 cache_timeout。文档为 date_posted 列出了 this-week 和 this-month 等值;为 sort_by 列出了 relevance、most-liked 和 date-posted。参数的准确名称和用途见TikTok API 路由参考。

服务的基础地址是 https://tiktok.evelode.com;密钥通过查询参数 license_key 传递。以下示例将密钥保存在环境变量 JSONSCRAPER_TIKTOK_LICENSE_KEY 中。这样可避免将机密直接写入源代码或输出到运行日志。

解析响应时,需要考虑其嵌套结构。先检查外层 status,再进入 tiktok 对象。搜索结果中的视频列表可能有两种形式:视频可能直接位于 tiktok.aweme_list 中,也可能位于 tiktok.search_item_list 各元素的 aweme_info 中。搜索响应里的 aweme_list 可能为空,但 search_item_list 中仍有视频。下面的脚本支持这两种形式。

文档和经过验证的响应示例显示,视频对象包含 aweme_id、desc、create_time、share_url、作者信息和 statistics 对象等字段。统计信息可能包括播放、点赞、评论和转发计数。每次都应检查实际字段及其含义;代码会将缺失字段留空。路由示例及其文档所列参数见TikTok Scraper API 说明。

Python 实用示例

示例适用于 Python 3.8 及以上版本,并使用 requests 包。运行 python -m pip install requests 安装该包。将代码保存为 collect_tiktok.py,在环境中设置密钥,然后传入搜索词运行脚本。脚本会创建 data 目录,保存原始 JSON,并将规范化的观察记录追加到 CSV 文件中。

import csv
import json
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
from urllib.parse import urlparse

import requests

BASE_URL = "https://tiktok.evelode.com"
ARCHIVE = Path("data/raw")
OUT_CSV = Path("data/observations.csv")


def get_videos(tiktok):
    """从已确认的 searchVideo 响应形式中收集视频。"""
    videos = []

    aweme_list = tiktok.get("aweme_list") or []
    if isinstance(aweme_list, list):
        videos.extend(item for item in aweme_list if isinstance(item, dict))

    search_items = tiktok.get("search_item_list") or []
    if isinstance(search_items, list):
        for item in search_items:
            if not isinstance(item, dict):
                continue
            aweme_info = item.get("aweme_info")
            if isinstance(aweme_info, dict):
                videos.append(aweme_info)

    # 去重时保留原有顺序,并保留没有 ID 的记录。
    result = []
    seen = set()
    for video in videos:
        video_id = video.get("aweme_id")
        if video_id and video_id in seen:
            continue
        if video_id:
            seen.add(video_id)
        result.append(video)
    return result


def fetch(keyword, count=20, region="US"):
    license_key = os.environ.get("JSONSCRAPER_TIKTOK_LICENSE_KEY")
    if not license_key:
        raise RuntimeError("请在环境变量中设置 JSONSCRAPER_TIKTOK_LICENSE_KEY")

    params = {
        "license_key": license_key,
        "keyword": keyword,
        "count": count,
        "region": region,
        "sort_by": "relevance",
    }
    response = requests.get(
        f"{BASE_URL}/searchVideo", params=params, timeout=60
    )
    response.raise_for_status()
    payload = response.json()

    if payload.get("status") != "ok":
        raise RuntimeError("TikTok API 返回了非 ok 状态")

    tiktok = payload.get("tiktok")
    if not isinstance(tiktok, dict):
        raise RuntimeError("响应中不包含 TikTok 结果对象")
    return payload, tiktok


def write_rows(keyword, videos, collected_at):
    OUT_CSV.parent.mkdir(parents=True, exist_ok=True)
    fields = [
        "collected_at", "keyword", "aweme_id", "create_time",
        "author_id", "author_name", "author_username", "description",
        "share_url", "play_count", "digg_count", "comment_count",
        "share_count", "collect_count",
    ]
    write_header = not OUT_CSV.exists()

    with OUT_CSV.open("a", newline="", encoding="utf-8") as file:
        writer = csv.DictWriter(file, fieldnames=fields)
        if write_header:
            writer.writeheader()

        for video in videos:
            author = video.get("author") or {}
            stats = video.get("statistics") or {}
            writer.writerow({
                "collected_at": collected_at,
                "keyword": keyword,
                "aweme_id": video.get("aweme_id"),
                "create_time": video.get("create_time"),
                "author_id": author.get("uid"),
                "author_name": author.get("nickname"),
                "author_username": author.get("unique_id"),
                "description": video.get("desc"),
                "share_url": video.get("share_url"),
                "play_count": stats.get("play_count"),
                "digg_count": stats.get("digg_count"),
                "comment_count": stats.get("comment_count"),
                "share_count": stats.get("share_count"),
                "collect_count": stats.get("collect_count"),
            })


def main():
    if len(sys.argv) </code></pre>

通过环境变量传入密钥。在 Bash shell 中,可用以下命令为当前会话设置密钥:

export JSONSCRAPER_TIKTOK_LICENSE_KEY="你的密钥"
python collect_tiktok.py "meal prep"

在 Windows PowerShell 中,可使用 $env:JSONSCRAPER_TIKTOK_LICENSE_KEY="你的密钥" 为当前进程设置变量,然后运行同一个 Python 文件。不要将密钥加入代码仓库、CSV、响应归档或错误消息。由于 API 通过查询参数接收密钥,请注意完整请求 URL 可能会进入 HTTP 客户端和基础设施的日志;应配置日志,排除机密信息。

如何阅读代码并检查结果

fetch 函数传递文档所列参数,并检查 HTTP 状态、外部状态值以及 tiktok 对象是否存在。它不会把任何格式正确的响应正文都视为成功结果:如果格式出乎意料,程序会以清晰的错误信息停止。这样更容易区分空结果与访问问题或结构变化。

get_videos 函数会合并直接列表 aweme_list 中的元素,以及嵌套在 search_item_list[].aweme_info 中的记录。然后,它会按 aweme_id 去重,因为同一视频可能同时出现在两个集合中。如果缺少 ID,仍会保留该记录,这样便于检查原始结果,而不会填入虚构值。

规范化过程会安全地读取嵌套对象。作者或统计信息可能缺失,因此代码会在读取前使用空对象作为默认值。相应的 CSV 单元格也会留空。这一点很重要:空单元格表示该次观察没有收到这个值;零则表示 API 确实返回了数值零。

归档会为每个请求生成独立的 JSON 文件。文件名包含 UTC 时间和经过清理的搜索词。CSV 保存便于分析的扁平记录;原始 JSON 则作为参考材料,可用于解析新字段或重新处理数据。无论是本地原型,还是之后迁移到数据库或对象存储,这种双格式都很实用。

检查首次运行时,请打开 JSON,确认 status 为 ok,其中包含 tiktok 对象,并且一种或两种支持的集合中含有元素。然后将 CSV 中的若干值与原始对象进行核对。尤其要确认计数器没有变成错误字符串、发布日期按原始值保存,而且链接确实对应同一视频 ID。

如果有效响应中没有视频,应将其保存为一次空结果,而不是代码故障。如果外部状态或 HTTP 响应表示出错,就不要将该记录加入分析观察;应先排除原因,再重新运行。生产流程还应记录 HTTP 状态码、搜索词、时间和简短错误信息,同时排除密钥及包含密钥的完整 URL。

分页与采集的可重复性

搜索支持使用 cursor 参数翻阅结果。需要获取下一页时,请从上一条响应中读取游标,并原样传递。游标是 API 提供的不透明值:应用应按原样保存和返回,不要根据行数计算,也不要将其视为页码。

初期监控可以对每个关键词只发起一次请求,并保持 count 不变。这更便于解释:团队知道每次运行保存的是一个有限的搜索结果快照。如果加入分页,请保存每一页及其游标,并为每次运行设置页数上限。这样可以防止意外响应导致无限循环。

date_posted 参数可将搜索限制在文档列出的某个时间范围内;sort_by 可选择按相关性、发布日期或点赞数排序。分析变化时,请同时记录这些设置。若查询使用了不同时间范围或排序方式,比较结果可能反映的是搜索条件变化,而不是主题本身的变化。

cache_timeout 参数用于控制响应缓存。文档说明,默认缓存窗口为 3600 秒;设为 0 可关闭缓存。应有意识地为定期监控选择配置,并将其保存在配置文件中。如果一小时内多次发起查询,而且需要区分该时间窗口内的响应,请先单独验证缓存行为,再将重复结果解读为“没有变化”。

比较每日观察记录

CSV 已经包含生成简单报告所需的基础数据。按搜索词和 aweme_id 分组,按 collected_at 排序,并且只比较两次观察中都存在的指标值。例如,播放量差值可计算为最近一次观察值减去上一次观察值。请保留两个原始数据点:正确解读结果需要绝对差值和两次观察之间的时间。

如果没有明确说明时间间隔,就不要把这个差值称为播放速度。如果一次运行在早上进行,而下一次是在几天后,该数值表示这段时间内累计的变化。要比较大致增速,可以用差值除以两次观察之间的实际时长;但结果仍是两个快照之间的平均变化速度,并非连续遥测数据。

请分别计算两类信号。第一类是当前查询结果中首次出现的新 ID;第二类是已观察视频的统计数据变化。新视频无论当前播放量多少,都可能值得核查;旧视频的增长则有助于发现某条内容仍在持续获得互动。这两类信号回答不同问题,适用于不同的编辑规划场景。

加入简单的数据质量检查:含有 ID 的行占比、含有描述的行占比、采集日期是否存在、每个查询词对应的唯一视频数量,以及缺失统计数据的数量。这些指标有助于在团队依据不完整文件下结论之前,发现流水线是否不再获得惯常结构。它们也能显示哪些搜索词为你的行业提供了有用样本。

第一份报告只需包含以下列:查询词、ID、发布日期、作者、描述、链接、当前播放量、上一快照播放量、差值以及两次观察之间的时间。再加入“档案中新增”标记和编辑评估字段。员工可以标记内容与品牌的相关性、形式是否适用,以及是否存在模仿他人创意的潜在风险——API 本身不会进行这些评估。

不要混淆视频热度与主题价值

播放、点赞、评论和转发数据有助于描述公众对某条视频的反应。内容规划还需要关注文本本身:问题的措辞、开头的承诺、反复出现的讲解形式或演示类型。因此,应将链接和描述连同指标一起交给编辑人员;单一数字很少足以解释一条内容为何有效。

比较多条视频时,如果两个计数器都存在,可以计算互动与播放量的比值。例如,将点赞、评论和转发数相加,再除以播放量,可得到一个简单的可观测反应占比指标。请记录所用公式,并排除缺失值视频,以免结果无法比较。该系数只描述所选视频和公开计数器。

不要把多次快照中的指标相加,仿佛它们代表互不相关的事件。同一条视频如果出现在五次每日响应中,这些行里的播放量是同一个计数器的连续状态,并非五组不同受众。要计算唯一发布内容的总量,应为每条视频选取一个一致的观察时点,例如报告期内的最后一次快照,并记录选择规则。

描述中反复出现的词语也是有用的分析角度。简单的文本分析可以根据预先设定的词表对内容分组,例如“食谱”“评测”“前后对比”“错误”“技巧”。分类器或语言模型可以提出类别并生成简短摘要,但表格中应保留原始描述和 URL,便于编辑人员根据视频本身复核解读。

按计划自动运行

手动运行验证通过后,可将采集任务交给调度工具:自有服务器上的 cron、GitHub Actions,或 n8n/Make 工作流。集成应运行同一个已验证脚本、从密钥存储中读取密钥、归档 JSON,并且只在成功完成后发送通知。具体调度方式取决于基础设施;API 调用和处理格式保持不变。

以下 cron 示例会按照机器本地时间,在每天 08:15 运行:

15 8 * * * cd /srv/tiktok-monitor && /usr/bin/python3 collect_tiktok.py "meal prep" >> logs/collector.log 2>&1

使用该配置前,请在 cron 运行所用的服务或 shell 中设置环境变量,并确保主机时区正确。对于多个关键词,最好将列表保存在文件中,并让脚本每次单独处理一个查询。这样,单个查询出错时不一定会影响全部任务,归档也更容易比较。

关键词数量增加后,可将配置移到 JSON 或 YAML 文件中,并添加运行日志,记录状态、获取的视频数量和归档路径。为提升可靠性,应针对临时网络故障设置有限次数的重试、超时以及单独的 HTTP 错误处理。不能无限重试:任务必须以明确状态结束,避免后续运行掩盖问题。

如果需要将结果写入 Google Sheets、数据库或内部面板,请在可靠归档之后再添加这一步。先保存原始响应,再上传规范化记录;如果上传失败,可以从归档重试,而不一定要重新采集。视频 ID 和采集时间有助于实现可重复写入,并区分新旧快照。

加入 AI:适合做整理,而不是替代来源

归档中积累大量描述后,模型可以帮助按主题归类视频、找出反复出现的措辞,并为编辑人员准备简短说明。只向模型提供必要字段:搜索词、描述、发布日期和公开计数器。应要求模型返回结构化类别和简短依据,而不是对数据无法证明的流行原因作出肯定判断。

将模型结果保存在独立列中,例如 ai_topic、summary 和 review_status。不要覆盖原始描述和 API 数值。修改指令或模型后,应能基于已保存记录重新处理;因此,建议记录提示词版本和分类日期。

模型的一项实用任务是为编辑人员提出内容类别,例如“快速食谱”“设备对比”“新手常见错误”。随后,分析人员可通过链接抽查各类别中的若干示例,并调整词表。这种人机协作流程让 AI 成为浏览内容集合的工具,同时保留可核查的原始观察记录。

不要让模型仅凭小规模搜索样本,就自行断言某种内容形式导致了更高覆盖量。要得出这种结论,需要预先确定方法、可比较的发布内容以及额外数据。在实际编辑工作中,只需对待查看对象排序、提出重复主题的假设,并生成供人工分析的链接清单即可。

样本质量与结论边界

这条流水线保存的是按指定查询词和参数获取的响应。它有助于比较这些观察结果,但搜索覆盖范围受具体查询词、采集时点、设置和服务响应限制。因此,结论应准确表述:“本周快照中,查询 X 所返回的结果里更常出现主题 Y”,而不是“TikTok 现在全平台都在讨论 Y”。

只比较设置一致的系列:相同地区、相同排序参数、发布日期范围和请求结果数。如果更换查询词或筛选条件,请将其标记为一个新系列。分析季节性或营销活动时,应保存配置版本,否则输入变化可能看起来像内容本身发生了变化。

公开指标可能在两次快照之间更新,而且其累积时间不同:新发布的视频和旧视频处于反应积累的不同阶段。比较表现时,请考虑发布日期和观察间隔。可以比较发布时长相近的内容,或分析同一 ID 在连续快照中的变化。

如果项目涉及竞争对手或创作者,请将采集限制在具体任务和最低必要数据范围内。不要采集敏感信息,也不要用公开指标评价创作者本人。营销团队的报告通常只需要链接、描述、账号名称、发布时间和汇总指标。自动化之前,请检查适用的平台条款、组织要求和数据留存规则。

运行前实用检查清单

  • 明确业务问题:你希望发现哪些主题、形式或发布内容。
  • 从少量稳定的搜索短语开始,并固定其写法。
  • 根据文档检查 /searchVideo 路由、必填参数 keyword 以及其他参数。
  • 将密钥保存在环境变量中,并避免将其写入输出、日志或代码仓库。
  • 检查响应的外部状态,并确认 tiktok 对象存在。
  • 同时处理 aweme_list 和 search_item_list[].aweme_info。
  • 在规范化之前保存原始 JSON。
  • 缺失字段留空,不要用零替代。
  • 按 aweme_id 匹配发布内容,并原样传递游标。
  • 为每个快照记录采集日期和搜索参数。
  • 在得出结论或将结果交给模型之前,人工检查若干视频。

最终产出

完成设置后,团队将拥有一套可重复的流程:定时运行关键词列表,将每次响应保存为原始 JSON,并将规范化观察记录追加到 CSV。分析人员可以查看档案中新增的视频、打开链接并查看描述、比较不同快照中可用的公开计数器,并创建主题分组。

编辑人员得到的不是自动生成的“头号趋势”判定,而是一份简短、可核查的候选清单:新发布的内容、指标显著变化的视频,以及反复出现的主题表述。这能缩短从手动搜索到讨论选题的时间,并为是否将某个主题纳入内容计划提供可追溯的依据。

可以从一个查询词、每天在本地运行一次以及一个 CSV 文件开始。确认样本符合需求后,再加入定时调度、数据库或描述分类功能。路由的文档参数和用途可查阅TikTok API 文档;访问和产品信息可见TikTok Scraper API jsonscraper 页面。

People

No people listed for this article yet.

Keep reading使用 Python 监测 TikTok 话题标签:可运行的 API 请求
Read the next article

将阅读内容转化为可用的集成

探索 jsonscraper 社交数据 API、测试请求并构建下一个工作流。

查看 API