jsonscraper
Рынки

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

Соберите выборку по ключевым словам, выделите потенциальные сигналы спроса и передайте проверяемые данные аналитику или ИИ-классификатору

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

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

Article: Как оценить качество TikTok-лидов по публичным видео: пошаговый пайплайн на Python
Summary: Практический сценарий для маркетолога и разработчика: искать публичные TikTok-видео по запросам аудитории через TikTok Scraper API, сохранять исходные ответы, дедуплицировать ролики и ранжировать их по явным правилам. Показан Python-скрипт с обработкой двух вариантов структуры ответа и безопасной передачей API-ключа.
Image role: cover — the central idea
Section: 
Visual subject: Close-up editoria
© jsonscraper · AI-generated illustration

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

Проблема: запросы аудитории есть, но исследование трудно повторить

Ручное исследование легко теряет контекст. Один сотрудник ищет «как добавить субтитры», другой — «автоматические субтитры для Reels», а третий сохраняет только понравившиеся ролики. В итоговой таблице смешиваются разные запросы, даты, метрики и критерии отбора. Когда руководитель просит повторить анализ или сравнить две недели, приходится восстанавливать не только результаты, но и сам ход поиска.

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

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

Схема решения: поиск, фиксация, очистка, оценка

Пайплайн состоит из четырёх этапов. Сначала команда формирует небольшой словарь поисковых запросов, каждый из которых соответствует конкретной задаче аудитории. Затем скрипт запрашивает поиск видео и сохраняет полный JSON-ответ. Третий этап нормализует разные структуры ответа в единую строку на видео и устраняет повторы по идентификатору. Наконец, правила или отдельный ИИ-классификатор присваивают приоритет для ручной проверки, сохраняя причину оценки.

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

В качестве базовых запросов можно взять «ошибки автоматических субтитров», «как быстро добавить субтитры» и «монтаж коротких видео для бизнеса». Это лишь примеры словаря: формулировки следует адаптировать к языку аудитории и продукту. Запросы нужно хранить вместе с результатом, потому что поиск по разным словам формирует разные выборки.

Что предоставляет TikTok Scraper API

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

Ключ передаётся как query-параметр license_key. В примере ниже он считывается из переменной окружения JSONSCRAPER_TIKTOK_LICENSE_KEY, чтобы не хранить секрет в коде и не выводить его в журнал. Для аутентификации и выбора параметров используйте текущие условия своего доступа; сама страница TikTok Scraper API содержит описание продукта и переходы к демонстрации и документации.

Ответ поиска необходимо разбирать с учётом структуры, а не предположения об одном-единственном массиве. В проверенном примере API возвращал верхнеуровневый статус и объект tiktok; ролики могут находиться в tiktok.aweme_list, а для результатов поиска также встречается tiktok.search_item_list[].aweme_info. Проверяйте обе формы. В найденных объектах доступны, если поле присутствует, идентификатор ролика, описание, ссылка, время публикации, сведения об авторе и статистика, например просмотры, лайки, комментарии и публикации. Метрики и поля нужно считать необязательными: код не должен подставлять вымышленные нули там, где значение отсутствует.

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

Подготовка окружения

Пример использует Python 3.8 или новее и стандартную библиотеку. Укажите ключ в окружении, например в оболочке macOS или Linux:

export JSONSCRAPER_TIKTOK_LICENSE_KEY="ваш_ключ"
python tiktok_demand.py

В Windows PowerShell переменную можно задать так:

$env:JSONSCRAPER_TIKTOK_LICENSE_KEY="ваш_ключ"
python .\tiktok_demand.py

Скрипт выполняет отдельный запрос для каждого ключевого слова. Он проверяет HTTP-ошибки, JSON-ответ и внешний статус, затем извлекает объекты из обоих подтверждённых вариантов массива. Сырые ответы записываются построчно в файл JSONL: каждая запись содержит запрос, время наблюдения и исходный ответ. Вторая часть формирует CSV с унифицированными полями. Файл ключа в коде отсутствует, а содержимое секрета не печатается.

Пример: сбор и нормализация результатов поиска

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

BASE_URL = "https://tiktok.evelode.com/searchVideo"
QUERIES = [
    "ошибки автоматических субтитров",
    "как быстро добавить субтитры",
    "монтаж коротких видео для бизнеса",
]


def extract_videos(payload):
    """Поддерживает обе наблюдавшиеся структуры поискового ответа."""
    tiktok = payload.get("tiktok") or {}
    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
            video = item.get("aweme_info")
            if isinstance(video, dict):
                videos.append(video)

    return videos


def fetch(query, license_key):
    params = {
        "keyword": query,
        "count": 20,
        "sort_by": "relevance",
        "date_posted": "this-month",
        "region": "US",
        "license_key": license_key,
    }
    url = BASE_URL + "?" + urlencode(params)
    request = Request(url, headers={"Accept": "application/json"})

    try:
        with urlopen(request, timeout=45) as response:
            payload = json.loads(response.read().decode("utf-8"))
    except HTTPError as exc:
        raise RuntimeError("API returned HTTP status {}".format(exc.code)) from exc
    except (URLError, TimeoutError, json.JSONDecodeError) as exc:
        raise RuntimeError("Could not complete or parse API request") from exc

    if payload.get("status") != "ok":
        raise RuntimeError("API response status was not ok")
    return payload


def metric(statistics, key):
    value = statistics.get(key)
    return value if isinstance(value, (int, float)) else ""


def main():
    license_key = os.environ.get("JSONSCRAPER_TIKTOK_LICENSE_KEY")
    if not license_key:
        raise SystemExit("Set JSONSCRAPER_TIKTOK_LICENSE_KEY in the environment")

    raw_path = Path("tiktok_raw.jsonl")
    csv_path = Path("tiktok_videos.csv")
    seen_ids = set()
    rows = []

    with raw_path.open("a", encoding="utf-8") as raw_file:
        for query in QUERIES:
            observed_at = datetime.now(timezone.utc).isoformat()
            payload = fetch(query, license_key)
            raw_file.write(json.dumps({
                "query": query,
                "observed_at": observed_at,
                "response": payload,
            }, ensure_ascii=False) + "\n")

            for video in extract_videos(payload):
                video_id = video.get("aweme_id")
                if not video_id or video_id in seen_ids:
                    continue
                seen_ids.add(video_id)

                author = video.get("author") or {}
                stats = video.get("statistics") or {}
                rows.append({
                    "query": query,
                    "observed_at": observed_at,
                    "video_id": video_id,
                    "created_at": video.get("create_time", ""),
                    "description": video.get("desc", ""),
                    "share_url": video.get("share_url", ""),
                    "author_username": author.get("unique_id", ""),
                    "author_nickname": author.get("nickname", ""),
                    "views": metric(stats, "play_count"),
                    "likes": metric(stats, "digg_count"),
                    "comments": metric(stats, "comment_count"),
                    "shares": metric(stats, "share_count"),
                })
            time.sleep(1)

    columns = [
        "query", "observed_at", "video_id", "created_at", "description",
        "share_url", "author_username", "author_nickname", "views",
        "likes", "comments", "shares",
    ]
    with csv_path.open("w", newline="", encoding="utf-8") as csv_file:
        writer = csv.DictWriter(csv_file, fieldnames=columns)
        writer.writeheader()
        writer.writerows(rows)

    print("Saved {} unique returned videos to {}".format(len(rows), csv_path))


if __name__ == "__main__":
    main()

Значение count здесь задано как запрошенный размер выборки на запрос. Скрипт не считает, что API обязан вернуть ровно столько элементов: фактическое число строк зависит от ответа. Пауза между запросами — простая мера управления темпом запуска, а не заявление о лимите или требовании сервиса. При промышленном расписании добавьте обработку повторных попыток с ограничением, мониторинг ошибок и правила для допустимой частоты запросов, ориентируясь на условия своего плана.

Как читать получившуюся таблицу

В CSV одна строка представляет один уникальный возвращённый ролик, найденный по одному из запросов. Поле query показывает происхождение записи, observed_at — время снимка, а video_id служит ключом дедупликации. Описание и ссылка помогают аналитику проверить контекст, а публичные счётчики — оценить отклик на момент сбора. Для долгосрочного сравнения используйте отдельные наблюдения: не перезаписывайте историю метрик, если требуется увидеть их изменение.

Счётчики просмотров, лайков, комментариев и репостов — снимок публичной статистики, возвращённый API в момент запроса. Они могут отсутствовать в отдельных объектах, поэтому пустое значение в CSV обозначает отсутствие числового поля в ответе. Оно отличается от нуля и должно обрабатываться отдельно при расчёте агрегатов. Кроме того, найденные результаты отражают выборку выдачи для конкретных слов, региона, временного фильтра и параметров запроса. Они описывают именно полученные ролики, а не полный объём контента или аудитории платформы.

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

Подключение ИИ без передачи ему роли судьи

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

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

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

Автоматизация по расписанию и контроль качества

После успешного ручного запуска скрипт можно запускать по расписанию в cron, планировщике задач облачной платформы или в оркестраторе вроде n8n. Практическая последовательность узлов остаётся той же: прочитать список запросов, вызвать API, проверить статус, сохранить сырой ответ, нормализовать ролики, записать новые снимки и сформировать отчёт. Передавать ключ следует через механизм секретов среды исполнения, а не в таблицу, текст workflow или сообщение об ошибке.

В автоматическом задании полезны четыре проверки. Первая — запрос завершился успешно и ответ является JSON. Вторая — внешний статус равен ожидаемому значению. Третья — обработчик понимает оба варианта структуры поиска и корректно переживает отсутствующие поля. Четвёртая — число новых уникальных роликов и число ошибок записываются в журнал. Если результатов внезапно нет, отчёт должен показать нулевую выборку как состояние запуска, а не смешивать её с обычным успешным отчётом без пояснения.

Пагинацию добавляйте после проверки первой страницы. Для маршрута поиска документация указывает параметр cursor; сохраняйте полученный токен и передавайте его без преобразования в следующем запросе. Ограничьте максимальное число страниц на один поисковый термин и остановите цикл при отсутствии новых элементов, отсутствии следующего курсора или повторе уже обработанного курсора. Такой предел делает время выполнения и размер сбора управляемыми. Факт наличия курсора позволяет запросить продолжение выдачи, но не устанавливает полноту результата относительно всех публикаций.

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

Метрики: что можно сравнивать корректно

Для операционного мониторинга полезны количество уникальных найденных роликов по запросу, доля роликов с заполненным описанием, распределение доступных реакций и частота появления определённых тем. Сравнивая запросы, фиксируйте одинаковые параметры и период запуска. Если один запуск использует this-week, а другой this-month, различие объёмов может объясняться временным окном, поэтому его нельзя интерпретировать как изменение интереса без дополнительного анализа.

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

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

Типовые ошибки и способы их избежать

Считать пустое поле нулевым значением

Если статистика или автор отсутствуют, код выше оставляет ячейку пустой. Это сохраняет различие между неизвестным значением и измеренным нулём. При аналитике явно задайте правило обработки пропусков и указывайте число строк, исключённых из конкретного расчёта.

Предполагать, что поиск всегда возвращает один массив

Для поискового ответа встречаются aweme_list и вложенные элементы search_item_list[].aweme_info. Обработчик проверяет обе формы и игнорирует элементы неподходящего типа. Если схема изменится, сохраняемые сырые ответы помогут быстро выявить новое расположение данных, а тестовые JSON-фикстуры — проверить исправление до следующего запуска.

Повторно считать один ролик отдельным сигналом

Один ролик может соответствовать нескольким поисковым словам. В примере он включается в итоговый CSV один раз, а поле запроса указывает первое совпадение в текущем порядке. Если важно сохранить все соответствия «ролик — запрос», храните отдельную связующую таблицу или список запросов для каждого идентификатора, а не удаляйте происхождение записи.

Сравнивать несопоставимые выборки

Слово, регион, временной фильтр, сортировка и дата запуска влияют на выборку. Сохраняйте параметры вместе с ответом и меняйте по одному фактору, когда исследование сравнивает варианты. Это помогает понять, связано ли изменение результата с поведением выдачи или с изменением настроек эксперимента.

Выдавать корреляцию за намерение купить

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

Как расширить пайплайн

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

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

В n8n сбор можно запускать по расписанию, а код нормализации выполнять отдельным Python-сервисом или узлом, если выбранная среда поддерживает нужные операции. Для первого этапа не обязательно встраивать модель: сначала добейтесь устойчивого сбора, затем измерьте, сколько времени занимает ручная классификация и какие категории повторяются. ИИ оправдан, когда он сокращает конкретную повторяющуюся работу и его метки можно проверять.

Если рабочий вопрос касается не поиска по формулировкам, а регулярного наблюдения за определённым аккаунтом, в официальном справочнике есть отдельный маршрут /getUserFeed с параметрами query, max_cursor и count. Для хэштега предусмотрен /getHashtagFeed с параметром cursor. Это разные сценарии сбора: выбор endpoint следует начинать с исследовательского вопроса, а не подменять один тип выборки другим.

Результат: повторяемая выборка вместо набора закладок

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

Практический итог для команды — более прозрачное решение о том, какие проблемы стоит проверять в интервью, посадочной странице или рекламном сообщении. Если исследование требует другого типа данных, смените запросы или используйте соответствующий документированный маршрут. Для проверки параметров перед адаптацией пайплайна откройте документацию TikTok API; сведения о продукте и демонстрационный вход находятся на странице TikTok Scraper API.

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

Персоны

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

Читайте дальшеКак архивировать результаты поиска TikTok на Python
Читать следующую статью

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

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

Смотреть API