jsonscraper

Как сравнить TikTok-креативы конкурентов по публичным метрикам: пайплайн на Python

Соберите результаты по нескольким поисковым запросам, уберите дубликаты и сравнивайте ролики по просмотрам, лайкам и комментариям

Редакционная политика Сообщить об ошибке

Две команды запускают ролики о похожем продукте, но оценивают их вручную по разным признакам: одна запоминает просмотры, другая — комментарии, а третья сохраняет ссылки в чате. Через неделю трудно понять, какие форматы повторяются у конкурентов, какие публикации набирают заметные реакции и что изменилось со времени прошлого просмотра.

Решение — собирать ограниченную и воспроизводимую выборку публичных видео по заранее заданным ключевым словам, сохранять исходный JSON и строить таблицу с едиными полями. Это позволяет сравнивать ролики в одном рабочем процессе, а не смешивать случайные наблюдения с выводами о рынке. Ниже — схема для небольшого конкурентного мониторинга на Python с TikTok Scraper API от jsonscraper.

Сначала определите вопрос анализа

Сбор данных полезен, когда заранее известно, какое решение будет принято по его результатам. Например, маркетинговая команда производителя спортивной бутылки хочет понять, как конкуренты показывают продукт в коротких роликах: через демонстрацию использования, распаковку, сравнение или пользовательскую реакцию. Для этого не требуется копировать весь TikTok. Нужны несколько целевых поисковых запросов и повторяемое наблюдение за видео, которые API возвращает по этим запросам.

Сформулируйте вопрос так, чтобы его можно было проверить: «Какие форматы описаний и демонстраций встречаются среди опубликованных роликов по нашим пяти ключевым фразам, и какие из найденных видео имеют наибольшие текущие значения просмотров и реакций?» Такая формулировка разделяет два слоя анализа. Текст и описание помогают сгруппировать творческие подходы; счётчики просмотров, лайков, комментариев и репостов дают снимок публичной реакции на момент сбора.

Этот снимок полезен для приоритизации ручного просмотра и отслеживания изменений. Он описывает ролики, возвращённые для конкретных запросов, параметров и времени запуска. Поэтому корректный вывод звучит как «в нашей выборке часто встречаются распаковки» или «у этой группы роликов сейчас выше медиана просмотров». Именно такой масштаб анализа помогает команде выбирать гипотезы для собственного контента.

Спроектируйте небольшой конвейер данных

Разделите работу на четыре этапа: сбор, сохранение сырого ответа, нормализация и анализ. Сбор обращается к API по каждому утверждённому ключевому слову. Сырой ответ сохраняется целиком для повторной проверки. Нормализация превращает вложенную структуру JSON в строки таблицы, а аналитика считает метрики и группирует ролики по ключевым словам или вручную присвоенным форматам.

  • Сбор. Запросите заданное число результатов по каждому ключевому слову с одинаковым регионом и сортировкой.
  • Архив. Запишите исходный JSON и время получения, чтобы можно было проверить извлечённые значения.
  • Нормализация. Оставьте идентификатор видео, ссылку, описание, автора, время публикации и доступные счётчики.
  • Контроль качества. Уберите дубликаты по ID, отметьте отсутствующие значения и сохраните ключевое слово, которое привело к ролику.
  • Сравнение. Рассчитайте собственные показатели только из доступных исходных полей и сравните группы в рамках одного снимка.

Такой подход лучше списка ссылок, потому что сохраняет происхождение строки и позволяет отличить повторное появление того же ролика от нового материала. Храните также дату сбора: публичные счётчики могут измениться, поэтому значение из сегодняшнего ответа нельзя трактовать как постоянную характеристику видео.

Что поддерживает TikTok Scraper API

Для поиска видео по текстовому запросу документация jsonscraper описывает GET-маршрут /searchVideo на базовом адресе https://tiktok.evelode.com. Обязательный параметр — keyword; доступны также count, cursor, region, date_posted, sort_by и cache_timeout. В документации указаны варианты сортировки most-liked, relevance и date-posted, а среди периодов публикации — yesterday, this-week, this-month, last-3-months, last-6-months и all-time. Сверяйте имена параметров и назначения с справочником маршрутов TikTok.

Ключ передаётся в query-параметре license_key. Для рабочего скрипта положите его в переменную окружения JSONSCRAPER_TIKTOK_LICENSE_KEY, а не в исходный файл, таблицу или журнал запуска. В ответе проверяйте верхнеуровневый status, затем разбирайте объект tiktok. Для поиска структура может содержать список aweme_list, а также search_item_list, где сведения о ролике расположены внутри aweme_info. Код ниже поддерживает обе формы.

В подтверждённых наблюдениях ответа поиска присутствуют ID ролика, описание, время публикации, ссылка, сведения об авторе и метрики вроде просмотров, лайков, комментариев, сохранений и репостов. Поля могут отсутствовать в конкретной записи. В таблице такие значения следует оставлять пустыми, а не подменять нулём: ноль означает измеренное значение, пустое поле — отсутствие значения в полученном объекте.

Минимальный пример на Python

Скрипт ниже выполняет один запрос на каждое ключевое слово, сохраняет полный ответ в JSON-файл и формирует CSV для таблицы или последующей обработки. Он рассчитан на Python 3.8+ и использует только стандартную библиотеку. Установите переменную окружения с лицензией перед запуском. Список ключевых слов и количество результатов настройте под свой сценарий.

import csv
import json
import os
import time
from datetime import datetime, timezone
from pathlib import Path
from urllib.parse import urlencode
from urllib.request import urlopen

BASE_URL = "https://tiktok.evelode.com/searchVideo"
LICENSE_KEY = os.environ["JSONSCRAPER_TIKTOK_LICENSE_KEY"]
KEYWORDS = ["термобутылка", "спортивная бутылка", "распаковка бутылки"]
REGION = "US"
COUNT = 20


def request_search(keyword):
    params = {
        "license_key": LICENSE_KEY,
        "keyword": keyword,
        "count": COUNT,
        "region": REGION,
        "sort_by": "relevance",
        "date_posted": "this-month",
    }
    url = BASE_URL + "?" + urlencode(params)
    with urlopen(url, timeout=60) as response:
        return json.loads(response.read().decode("utf-8"))


def extract_videos(payload):
    if payload.get("status") != "ok":
        raise RuntimeError("API returned a non-ok status")

    tiktok = payload.get("tiktok") or {}
    rows = []

    for item in tiktok.get("aweme_list") or []:
        if isinstance(item, dict):
            rows.append(item)

    for item in tiktok.get("search_item_list") or []:
        video = item.get("aweme_info") if isinstance(item, dict) else None
        if isinstance(video, dict):
            rows.append(video)

    return rows, tiktok


Path("raw").mkdir(exist_ok=True)
seen = set()
records = []

for keyword in KEYWORDS:
    payload = request_search(keyword)
    collected_at = datetime.now(timezone.utc).isoformat()
    safe_name = keyword.replace(" ", "_")
    raw_path = Path("raw") / (safe_name + ".json")
    raw_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")

    videos, tiktok = extract_videos(payload)
    for video in videos:
        video_id = video.get("aweme_id")
        if not video_id:
            continue
        dedupe_key = (keyword, video_id)
        if dedupe_key in seen:
            continue
        seen.add(dedupe_key)

        stats = video.get("statistics") or {}
        author = video.get("author") or {}
        records.append({
            "collected_at": collected_at,
            "keyword": keyword,
            "video_id": video_id,
            "url": video.get("share_url"),
            "created_at_epoch": video.get("create_time"),
            "description": video.get("desc"),
            "author": author.get("nickname"),
            "username": author.get("unique_id"),
            "views": stats.get("play_count"),
            "likes": stats.get("digg_count"),
            "comments": stats.get("comment_count"),
            "shares": stats.get("share_count"),
            "saves": stats.get("collect_count"),
            "cursor": tiktok.get("cursor"),
        })

    time.sleep(1)

fieldnames = [
    "collected_at", "keyword", "video_id", "url", "created_at_epoch",
    "description", "author", "username", "views", "likes", "comments",
    "shares", "saves", "cursor",
]
with open("tiktok_creatives.csv", "w", newline="", encoding="utf-8") as output:
    writer = csv.DictWriter(output, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(records)

print("Saved", len(records), "video rows to tiktok_creatives.csv")

Скрипт сохраняет ключевое слово рядом с видео: это важно, если один ролик обнаружен несколькими запросами. Здесь дедупликация выполняется внутри каждого ключевого слова, поэтому один ID может занимать отдельную строку для разных запросов. Для единой таблицы без повторов замените ключ (keyword, video_id) на video_id, а ключевые слова накапливайте в списке или отдельной таблице связей.

Код намеренно делает один запрос для каждого слова и не перебирает страницы автоматически. Если требуется следующая страница, документация определяет cursor как значение для прокрутки результатов, получаемое из предыдущего ответа. Передавать его следует как непрозрачное значение, не вычисляя самостоятельно. Для конкретного рабочего процесса проверьте схему пагинации на тестовом ответе и остановитесь при отсутствии новых результатов или следующего курсора.

Как получить сравнимую таблицу

CSV можно открыть в электронной таблице, загрузить в базу данных или использовать в Python для расчёта агрегатов. Для первичного отчёта сгруппируйте строки по дате сбора, ключевому слову и формату ролика. Формат лучше размечать после просмотра описания и самого видео: например, «демонстрация», «распаковка», «отзыв», «сравнение». Классификацию можно сделать вручную на небольшой выборке или предложить модели как черновую разметку с последующей проверкой человеком.

Если делаете автоматическую разметку текстов, передавайте модели только нужные поля — например, описание и идентификатор — и просите вернуть одну метку из заранее утверждённого списка. Сохраните исходное описание отдельно от результата классификации. Так аналитик сможет проверить спорную метку и пересчитать отчёт после изменения таксономии, не отправляя повторный запрос к API.

Для сравнительных метрик используйте значения из одного времени сбора. Например, отношение лайков к просмотрам можно вычислить как likes / views, когда оба поля присутствуют и просмотры больше нуля. Аналогично можно посчитать долю комментариев или репостов. Эти отношения описывают текущий публичный счётчик данного видео; они не измеряют охват конкретной аудитории, досмотры или конверсию. Для бизнес-решения сопоставляйте их с собственной кампанией и используйте как ориентир для выбора идей, а не как обещанный результат повторения чужого формата.

Медиана часто удобнее среднего, если в выборке есть несколько очень крупных роликов. Сравнивайте медиану просмотров и медиану реакций по группам только при одинаковом правиле сбора: тот же набор запросов, регион, временной фильтр и близкое время снимка. Записывайте параметры запуска вместе с датой. Если изменение поиска или выборки меняет состав строк, различие между двумя отчётами может отражать и изменение метода.

Повторный запуск и контроль изменений

Чтобы превратить разовый анализ в мониторинг, запускайте скрипт по расписанию — например, ежедневно или раз в неделю — и добавляйте дату сбора к каждой записи. Для каждого ролика храните пары «время снимка — значение счётчика». Тогда можно увидеть, как публичные показатели изменились между наблюдениями, сохраняя каждую точку измерения отдельно. Не перезаписывайте старые счётчики текущими: иначе динамика исчезнет.

В продакшене перенесите сырой JSON из локальной папки в объектное хранилище или другую систему архивации, а нормализованные строки — в базу с уникальным ключом, например сочетанием ID видео и времени сбора. Добавьте журнал ошибок без секретного ключа, ограничение числа повторов и уведомление при неуспешном запросе. При временной ошибке можно повторить запрос с задержкой; при структурной ошибке лучше сохранить ответ и отметить запуск как требующий проверки, чтобы сбой не превратился в тихо пустой отчёт.

Не используйте значение cursor как постоянный идентификатор исследования: это маркер продолжения выдачи. Храните его вместе с параметрами соответствующего запроса, используйте только для продолжения этой выдачи и не смешивайте страницы от разных ключевых слов. Также фиксируйте фактическое число обработанных результатов: запрошенное значение count задаёт желаемый размер ответа, а не гарантирует полный охват всех подходящих публикаций.

Как расширить рабочий процесс

Когда поисковая выборка слишком шумная, можно отдельно отслеживать конкретные открытые профили или хэштеги. В документации присутствуют маршруты /getUserFeed и /getHashtagFeed с параметрами запроса и пагинации. Это другие выборки с собственными правилами продолжения: для ленты профиля используется max_cursor, для хэштега — cursor. Подключайте их как отдельные источники, сохраняйте тип источника и параметры, чтобы не смешивать результаты поиска по ключевому слову с публикациями профиля или хэштега.

Для команды без собственной инфраструктуры CSV можно передавать в Google Sheets через API: документация Google Sheets API описывает чтение и запись значений ячеек. Добавьте отдельные листы для сырых запусков, нормализованных роликов и сводки. Такой вариант удобен для совместной разметки; когда объём истории станет неудобен для таблицы, перенесите строки в SQL-хранилище, сохранив те же поля и происхождение данных.

Для AI-агента разумная роль — подготовить краткое резюме уже собранных и нормализованных записей: сгруппировать описания, предложить форматы и выделить необычные значения. Агенту не стоит поручать формулирование фактов о всей платформе по небольшой выдаче. Передавайте ему период, список запросов и число строк, просите ссылаться на ID видео и возвращать неопределённые или отсутствующие поля как пустые. Затем аналитик проверяет примеры по сохранённым ссылкам.

Что получится на выходе

После настройки команда получает воспроизводимый реестр найденных видео с идентификаторами, ссылками, описаниями, авторами, временем публикации и доступными публичными счётчиками. Сохранённый сырой ответ даёт возможность перепроверить преобразование, а CSV позволяет быстро сортировать ролики, фильтровать по ключевым словам и сравнивать снимки по датам. Из этого можно сделать еженедельную сводку: какие форматы повторялись, какие публикации заслуживают просмотра и какие творческие гипотезы стоит протестировать в собственном контенте.

Практический результат — не универсальный рейтинг конкурентов, а дисциплинированная выборка с понятным происхождением. Её качество зависит от выбранных запросов, фильтров и периода наблюдения. Поэтому сохраняйте параметры сбора, проверяйте отдельные ролики вручную и отделяйте доступные метрики от интерпретации. Такой процесс помогает заменить разрозненные закладки сравнимыми данными и обсуждать креативы на конкретных примерах.

Маршрут поиска и параметры можно проверить в официальной документации jsonscraper, а описание сервиса и доступ к нему находятся на странице TikTok Scraper API. Начните с нескольких поисковых фраз, сохраните один тестовый ответ и проверьте структуру перед настройкой расписания.

Персоны

Для этой статьи персоны пока не указаны.

Читайте дальшеКак находить ранние сигналы TikTok-трендов: автоматический мониторинг поисковой выдачи
Читать следующую статью

Превратите прочитанное в рабочую интеграцию

Изучайте API социальных данных jsonscraper, тестируйте запросы и создавайте новые процессы.

Смотреть API