返回博客

使用个人主页快照跟踪 TikTok 粉丝增长

Emily Chen
Emily Chen

Advanced Data Extraction Specialist

03-Sep-2026

TL;DR:

  • TikTok 粉丝增长跟踪在开始收集时开始。 个人资料演员返回当前的公开计数,而不是历史序列。
  • 每个观察都需要收集状态。 失败的收集和未改变的粉丝数量是不同的结果。
  • 帐户身份应在用户名更改时得以保留。account_idsec_uid 和观察到的 unique_id 一起存储。
  • 增长是可比较快照之间的差异。 对相同帐户的连续有效计数进行相减,并保留两个时间戳。
  • 粉丝增长并不识别其原因。 变化不能归因于某个视频或活动,除非有单独的证据。
  • 免费开始。 新的 Scrapeless 帐户通过 Scrapeless Dashboard 包含免费信用。

引言:个人资料计数在第二次观察后变得有用

公开的粉丝计数是一个快照。只有在再次收集同一帐户并比较两个有效观察后,它才成为增长记录。

本教程使用 Scrapeless 个人资料演员、Python 和 SQLite 构建了一个小型 TikTok 粉丝增长跟踪管道。它保留帐户标识符,分别保存成功和失败的收集状态,从连续有效快照计算变化,并为创作者增长仪表板生成行。

底层个人资料字段和身份密钥在 TikTok 个人资料抓取教程 中介绍。

管道概览

阶段 操作 持久输出
定义 提供要追踪的帐户 帐户名册
收集 请求每个当前公开个人资料 原始 JSON 或错误记录
规范化 提取身份和统计信息 个人资料快照行
比较 连接连续成功快照 粉丝变化行
报告 画出水平和变化 增长仪表板输入

管道从开始收集的那天起便创建了历史。它不会从第一个存储观察之前的日期重建粉丝计数。

前提条件

  • 一个来自 Scrapeless Dashboard 的 Scrapeless 帐户和 API 令牌
  • 一个用户维护的公开 TikTok 用户名列表
  • 适合报告需要的收集计划
  • SCRAPELESS_API_KEY 中的一个有效令牌,用于下面的 API 块

完整示例有一个前提条件缺口,因为读者必须提供一个有效的凭据和帐户名册。本文不嵌入凭据或提供虚构的个人资料响应。

阶段 1:定义帐户名册

名册应包含一个稳定的内部键、当前用户名、跟踪该帐户的业务原因,以及记录是否保持激活状态。用户名是方便的请求输入,但不应成为唯一的数据库键。

scraper.tiktok.user.detail 演员接受 unique_id,而不带领导的 @。它的响应可以包括 account_idunique_idsec_uid、公开个人资料字段、帐户标志和包含粉丝、关注、朋友、喜欢、视频和喜欢视频计数的 statistics 对象。

将看起来像数字的帐户 ID 保留为字符串。保留所有三个标识符,以便稍后的用户名更改不会默默地创建一个新的历史序列。

阶段 2:捕获带时间戳的个人资料快照

每个收集尝试需要一个应用程序时间戳和状态。成功的行存储返回的标识符和统计信息。失败的尝试存储帐户键、尝试时间和一个简明的错误状态,而不将缺失的计数复制为零。

Python的 datetime 文档 描述了具有时区意识的时间戳。在存储中使用一个时区标准,并仅在报告层中应用显示时区。

TikTok的 官方用户信息文档 也将用户字段视为当前响应,而不是帐户历史馈送。因此,Scrapeless 管道构建了自己的观察历史。

使用 Scrapeless 开始抓取

使用 Scrapeless 为您的网络抓取和自动化工作流程加速!
今天注册并获得 5美元的免费信用无需信用卡

立即在 Scrapeless Dashboard 领取您的免费信用。

阶段 3:分别存储成功和收集缺口

SQLite 支持一个紧凑的附加只读快照表。SQLite 表约束文档 解释了用于防止重复观察的复合键。

注意:以下代码需要一个在 SCRAPELESS_API_KEY 中的实时 Scrapeless API 令牌和 TIKTOK_USERNAMES 中以逗号分隔的公开用户名。

python Copy
import json
import os
import sqlite3
from datetime import datetime, timezone
from urllib.error import HTTPError
from urllib.request import Request, urlopen

ENDPOINT = "https://api.scrapeless.com/api/v1/scraper/request"
TOKEN = os.environ["SCRAPELESS_API_KEY"]
USERNAMES = [v.strip().lstrip("@") for v in os.environ["TIKTOK_USERNAMES"].split(",") if v.strip()]


def get_profile(username):
    body = json.dumps({
        "actor": "scraper.tiktok.user.detail",
        "input": {"unique_id": username},
    }).encode()
    request = Request(
        ENDPOINT,
        data=body,
        headers={"x-api-token": TOKEN, "content-type": "application/json"},
        method="POST",
    )
    with urlopen(request, timeout=60) as response:
        return json.load(response)


database = sqlite3.connect("tiktok-profile-history.sqlite3")
database.execute("""
CREATE TABLE IF NOT EXISTS profile_snapshots (
    roster_username TEXT NOT NULL,
    collected_at TEXT NOT NULL,
    collection_status TEXT NOT NULL,
    account_id TEXT,
    unique_id TEXT,
    sec_uid TEXT,
    followers INTEGER,
    following INTEGER,
    likes INTEGER,
    videos INTEGER,
    error_code TEXT,
    raw_json TEXT,
    PRIMARY KEY (roster_username, collected_at)
)
""")

for username in USERNAMES:
    collected_at = datetime.now(timezone.utc).isoformat()
    try:
        profile = get_profile(username)
        statistics = profile.get("statistics") or {}
        database.execute(
            "INSERT INTO profile_snapshots VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
            (
                username, collected_at, "success",
                str(profile.get("account_id") or ""), profile.get("unique_id"),
                profile.get("sec_uid"), statistics.get("followers"),
                statistics.get("following"), statistics.get("likes"),
                statistics.get("videos"), None,
                json.dumps(profile, ensure_ascii=False),
            ),
        )
    except HTTPError as error:
        database.execute(
            "INSERT INTO profile_snapshots VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
            (username, collected_at, "failed", None, None, None, None, None, None, None,
             str(error.code), None),
        )

database.commit()
database.close()

collection_status 列保持一个可见的间隙。它防止报告查询将不可用观察视为没有关注者更改。

阶段 4:计算来自连续有效快照的增长

关注者增长是后期有效计数减去同一 account_id 的早期有效计数。保留开始和结束时间戳,两个源计数,差异以及经过的持续时间。

在连接时使用账户身份而非用户名。如果观察到的用户名在 account_id 保持稳定时发生变化,保留一系列并保留每个快照中记录的句柄。如果身份字段冲突,停止连接并将该行发送以供审核。

SQL 窗口函数可以配对成功的行而不覆盖原始观察:

sql Copy
SELECT
  account_id,
  unique_id,
  collected_at,
  followers,
  followers - LAG(followers) OVER (
    PARTITION BY account_id ORDER BY collected_at
  ) AS follower_change
FROM profile_snapshots
WHERE collection_status = 'success' AND followers IS NOT NULL;

结果度量存储观察之间的变化。除非观察的时间间隔与报告标签相同,否则它不是每日增长率。

阶段 5:构建创建者增长仪表板

一个有用的仪表板在增长旁显示数据质量:

  • 最新有效关注者数量及其收集时间
  • 自上一个有效快照以来的变化
  • 跨一个明确标记的报告间隔的变化
  • 成功观察的数量和收集的间隙
  • 当前观察到的用户名加上稳定的账户 ID
  • 首个存储观察日期,定义历史边界

单独绘制关注者水平和关注者变化。水平图显示累积系列;变化图使收集间隙和突然差异更易于审查。

负责任地处理个人资料数据

公共个人资料统计数据仍然可以是个人数据。将账户名册限制于声明的目的,限制原始响应访问,并定义旧快照何时应被删除。NIST 隐私框架 提供了识别和管理收集风险的结构。

不要从个人资料响应中推断受众人口统计、关注者身份或增长原因。计数变化可能与一个活动或帖子同时发生,但巧合并不是归因。

Scrapeless 通过 Scraping API 暴露个人资料演员。在设置账户数量和时间表之前,请检查 当前定价页面

结论:历史从记录的基线开始

TikTok 关注者增长跟踪需要稳定的账户身份、带时间戳的有效计数和明确的收集间隙。一旦基线存在,连续快照可以支持增长曲线,而无需声称不可用的平台历史或将变化归因于一条内容。

准备构建个人资料统计追踪器吗?

加入 Scrapeless DiscordTelegram 社区 以比较快照 схемы。 当账户名册准备好时,在 Scrapeless Dashboard 中创建一个账户。

常见问题

问:TikTok 关注者追踪器能恢复历史计数吗?

工作流程不能恢复收集开始前的计数。它的第一个成功观察成为后续比较的基线。

问:没有增长和收集失败有什么区别?

没有增长意味着两个有效快照包含相同的关注者计数。收集失败意味着后续计数未知,不应计算差异。

问:关注者增长能归因于一条 TikTok 视频吗?

关注者增长不能仅通过个人资料快照归因于一条视频。归因需要一个单独的实验或第一方活动证据。

问:工作流程是否暴露关注者身份或人口统计?

个人资料演员返回公共账户统计,而不是关注者列表或受众人口统计。

问:我需要管理代理或个人资料页面解析器吗?

演员在 API 请求背后处理其收集表面。调用者管理名册、时间表、存储、比较和报告。

问:收集公共关注者计数合法吗?
合法性取决于管辖权、用途、访问方法、数据和适用条款。尽量减少收集,记录目的,并为特定用途获得法律建议。

在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。

最受欢迎的文章

目录