Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TikTok Audit Monitor

Модульный инструмент social listening / OSINT для внутреннего аудита: собирает из TikTok описания видео и комментарии, минимизирует персональные данные и складывает в data platform (JSONL / Postgres). Заточен под поиск жалоб клиентов, схем обхода контролей и возможных утечек по упоминаниям бренда.

Сбор отделён от классификации и от хранилища. Источник данных (yt-dlp, прямой парс HTML, TikTokApi, импорт выгрузки или платный API) — это один сменный адаптер; вся обвязка (PII-минимизация, дедуп, классификация, выгрузка) общая. Когда TikTok что-то меняет — чините один коллектор, остальное не трогаете.


Содержание


Статус: что собирается, а что нет

Возможность Статус Канал
Дискавери по аккаунту @user (вся лента) ✅ работает с любого IP yt-dlp
Дискавери по ссылке на видео / id ✅ работает с любого IP yt-dlp
Описания + метаданные (лайки, просмотры, дата, музыка) ✅ работает с любого IP yt-dlp / прямой парс HTML
Импорт выгрузки JSON/CSV ✅ работает всегда manual
Дискавери по #хэштегу / ключевику ⚠️ см. ниже TikTokApi (подписанный API)
Комментарии ⚠️ см. ниже TikTokApi (браузер)
Классификация (жалоба/лазейка/утечка) 🔲 заглушка classify.py

Почему так. Описания и метаданные TikTok отдаёт прямо в HTML страницы видео (виден с любого IP). Комментарии и поиск — это подписанные XHR-эндпоинты, которые TikTok гейтит сильнее. См. следующий раздел.


Важно про TikTokApi (комментарии/поиск)

Сбор комментариев и поиск по хэштегам/ключевикам идут через библиотеку TikTokApi (Playwright-браузер, который сам подписывает запросы). На практике эти эндпоинты нестабильны:

  • Проверено на TikTokApi 7.3.3 (последняя на момент сборки) с валидным ms_token залогиненного аккаунта, с резидентного IP: рабочим оказался только trending, а user / hashtag / search / comments возвращали пустой ответ (EmptyResponseException) детерминированно — то есть это не репутация IP, а расхождение подписи библиотеки с текущими требованиями TikTok.
  • Это не чинится обновлением версии, прокси или токеном со стороны этого кода.

Вывод для боевого использования: для надёжного сбора комментариев/хэштегов в проде используйте платный TikTok-API (Apify TikTok Scrapers, EnsembleData, TikAPI, ScrapTik и т.п.) — они держат рабочий signer + пул прокси. Подключается это одним адаптером (см. Расширение); pipeline не меняется.

Встроенный путь TikTokApi оставлен как опциональный: он заработает, если у вас сложится связка «доверенный IP + рабочая на тот момент версия библиотеки + валидный ms_token». Чтобы проверить своё окружение — см. Диагностика.

Код при блокировке не падает: описания всё равно собираются, а блок комментариев пишется в stats.errors с инструкцией; повторный блок отключает комментарии на прогон, чтобы не долбить заблокированный IP.


Установка

git clone <your-repo-url> tiktok-audit-monitor
cd tiktok-audit-monitor

python3 -m venv .venv && source .venv/bin/activate
pip install -e .                       # ядро: описания, метаданные, manual-импорт

# Опционально — комментарии и keyword/hashtag-поиск (тяжёлые зависимости):
pip install -e ".[comments]"
python -m playwright install webkit

Требуется Python ≥ 3.11.


Быстрый старт

После pip install -e . доступна команда tiktok-audit-monitor (или python -m tiktok_audit_monitor.cli).

# 1) Что резолвится по запросу (без скачивания контента)
tiktok-audit-monitor discover --query "@tiktok" --limit 5

# 2) Описания из ленты аккаунта (работает с любого IP)
tiktok-audit-monitor collect --query "@some_account" --videos 20 --no-comments

# 3) Описания по конкретной ссылке, движок прямого HTML-парса (без yt-dlp)
tiktok-audit-monitor collect --query "https://www.tiktok.com/@user/video/123" \
    --engine html --no-comments

# 4) Описания + комментарии по ключевику (нужны TT_PROXY / TT_MS_TOKEN, см. выше)
TT_PROXY="http://user:pass@host:port" TT_MS_TOKEN="..." \
tiktok-audit-monitor collect --query "сбербанк лимит" --videos 10 --comments 50

# 5) Прогон выгрузки через pipeline (без живого скрейпинга — воспроизводимо)
tiktok-audit-monitor manual --input export.json

# 6) Только упоминания бренда
tiktok-audit-monitor collect --query "@some_account" --videos 50 --brand-filter

Выход: data/collected.jsonl (минимизированные записи сбора) и data/incidents.jsonl (пока все unclassified). Флаг --postgres-dsn дублирует в БД.


Конфигурация (env)

Переменная Назначение Дефолт
TT_PROXY egress-прокси (для комментариев/поиска нужен резидентный/мобильный)
TT_MS_TOKEN session-токен TikTok (для TikTokApi-путей)
TT_BROWSER webkit / chromium webkit
TT_HEADLESS 0 повышает шанс пройти анти-бот 1
TT_MIN_INTERVAL / TT_JITTER паузы между запросами (вежливость), сек 2.0 / 1.0
TT_MAX_RETRIES / TT_BACKOFF_BASE ретраи и бэкофф 4 / 2.0
TT_SOCKET_TIMEOUT таймаут сети, сек 30
TT_AUDIT_SALT соль для хэша автора. Не задана → генерится в data/.salt (с предупреждением), чтобы хэши не были обратимы по известной константе авто
TT_SALT_FILE путь к файлу соли (независим от TT_RAW_DIR) data/.salt
TT_STORE_TEXT хранить замаскированный текст 1
TT_RAW_STORE писать аудит-трейл сырых ответов (содержит ПДн!) 0 (выкл)
TT_RAW_DIR каталог сырого аудит-трейла data/raw
TT_COLLECTED / TT_INCIDENTS пути выгрузки data/collected.jsonl / data/incidents.jsonl

Скорость регулируется паузами: TT_MIN_INTERVAL=1 ускоряет в 2–3 раза, но повышает риск бана. При дефолте описания идут ~10 роликов/мин; комментарии ~10–15/мин плюс разовый старт браузерной сессии (5–15 с).


Формат вывода

collected.jsonl — по записи на строку (CollectedItem):

{
  "item_id": "a1b2c3…",
  "kind": "caption",                 // caption | comment | reply
  "video_id": "7649091368656194847",
  "source_url": "https://www.tiktok.com/@user/video/7649091368656194847",
  "author_hash": "9bdfc9a04c0d2cc3", // салт-хэш, не реальный ник
  "text": "…замаскированный текст…",  // телефоны/карты/email/контакты → [PHONE] и т.п.
  "like_count": 4026,
  "created_at": "2026-05-01T10:00:00Z",
  "collected_at": "2026-06-11T08:00:00Z",
  "collector": "ytdlp"
}

incidents.jsonlIncident с category/severity (сейчас null, т.к. классификатор — заглушка) и item_id-ссылкой на запись сбора.

Маскируются: телефоны (в т.ч. без кода страны и 10-значные счета РФ), номера карт, email, мессенджер-контакты (telegram/viber/whatsapp/…), @-хендлы. Автор хэшируется с солью. Сырой текст в выгрузку не попадает (только при TT_STORE_TEXT=1, и то замаскированный).


Архитектура

config.py        Settings (из env) + бренд-триггеры; resolve_salt()
models.py        VideoRef → RawItem (transient, c PII) → CollectedItem / Incident (persisted)
pii.py           хэш автора + маскировка телефон/карта/email/контакт; excerpt по span'ам токенов
classify.py      ЗАГЛУШКА (passthrough) — заменяется на правила/LLM, контракт не меняется
discovery.py     query → [VideoRef]; роутинг yt-dlp (user/video) vs TikTokApi-search (tag/keyword)
pipeline.py      оркестрация: discovery → collect → PII-min → (classify) → records; latch'и блокировок
sink.py          JsonlSink (идемпотентный дедуп) + Postgres upsert
cli.py           подкоманды discover / collect / manual
core/
  http_client.py httpx + rate limit + ретраи/бэкофф + прокси + ротация UA
  rate_limiter.py min-interval + jitter (детерминируемый — инъекция часов в тестах)
  rawstore.py    опциональный аудит-трейл сырых ответов (ПДн → ограничьте доступ)
  proxy.py       URL-строка прокси → Playwright ProxySettings {"server": …}
  errors.py      типизированные исключения (BlockedByAntiBot, DependencyMissing, …)
collectors/
  ytdlp.py       дискавери (user/video) + описания/метаданные         [работает с любого IP]
  html.py        прямой парс __UNIVERSAL_DATA_FOR_REHYDRATION__        [работает с любого IP]
  comments.py    комментарии через TikTokApi (прокси/ms_token)         [см. раздел про TikTokApi]
  search.py      keyword/hashtag-дискавери через TikTokApi             [см. раздел про TikTokApi]
  manual.py      импорт JSON/CSV                                        [работает всегда]

Каждый коллектор реализует крошечный протокол (DiscoveryCollector / ContentCollector в collectors/base.py) — движки взаимозаменяемы.


Расширение: свой источник (адаптер)

Чтобы подключить платный TikTok-API (или любой другой источник), не трогая pipeline, добавьте content-коллектор, возвращающий RawItem:

# collectors/myprovider.py
from ..models import RawItem, VideoRef

class MyProviderCollector:
    name = "myprovider"

    def __init__(self, settings, api_key: str):
        self.settings = settings
        self.api_key = api_key

    def collect(self, ref: VideoRef, *, comment_limit: int = 50):
        # 1) дёрнуть API провайдера по ref.video_id / ref.url
        # 2) вернуть RawItem'ы (kind="caption"/"comment"), сырой текст и автор —
        #    PII-минимизация и дедуп произойдут дальше в pipeline автоматически
        for c in provider_fetch_comments(self.api_key, ref.video_id, comment_limit):
            yield RawItem(kind="comment", text=c["text"], url=ref.url,
                          video_id=ref.video_id, author=c["author"],
                          like_count=c.get("likes"), collector=self.name)

Дальше — подменить выбор коллектора в pipeline.Pipeline._caption_collector_for / _comments_collector_for (или прокинуть фабрику). Вся остальная цепочка (маскировка, дедуп по item_id, выгрузка, классификация) работает как есть.


PII и 152-ФЗ

  • В хранилище попадают минимизированные записи: автор → салт-хэш, контактные данные в тексте замаскированы, сырой текст не сохраняется (или только замаскированный).
  • Сырой аудит-трейл (TT_RAW_STORE=1, каталог data/raw) содержит ПДн — включайте только при необходимости, ограничьте доступ и срок хранения по политике 152-ФЗ.
  • TT_AUDIT_SALT обязательно задайте явно в проде (иначе генерится случайная локальная соль; известная константа-заглушка никогда не используется для боевых хэшей).
  • Оформляйте обработку как цель предотвращения мошенничества; держите низкий объём запросов.

Тесты и диагностика

Юнит-тесты (stdlib, без pytest) — офлайн-логика сбора: роутинг запросов, маскировка ПДн, дедуп, нормализация комментария, парс HTML-рехидрации, классификация исключений, прокси-формат, rate limiter, сквозной manual-прогон:

python -m tiktok_audit_monitor.tests.run_tests

Диагностика окружения (diag_tiktok.py) — проверяет, какие TikTokApi-эндпоинты открыты с вашего IP+токена (откроется окно webkit):

python diag_tiktok.py "<ваш ms_token>"

Если trending OK, а comments/hashtag EMPTY — вы упёрлись в ограничение TikTokApi (см. раздел про TikTokApi); переходите на платный источник через адаптер.


Правовая оговорка

Неофициальный сбор данных нарушает Terms of Service TikTok и затрагивает обработку персональных данных третьих лиц. Используйте инструмент как внутренний аудиторский: низкий объём запросов, цель — предотвращение мошенничества, минимизация и ограниченное хранение данных. manual-источник переносит вопрос легальности сбора на тот процесс, которым получена выгрузка. Ответственность за законность использования — на операторе.


Структура проекта

tiktok-audit-monitor/
├── README.md
├── LICENSE
├── pyproject.toml
├── requirements.txt
├── diag_tiktok.py              # диагностика TikTokApi-эндпоинтов
└── tiktok_audit_monitor/       # пакет
    ├── cli.py, config.py, models.py, pii.py, classify.py
    ├── discovery.py, pipeline.py, sink.py
    ├── collectors/             # ytdlp, html, comments, search, manual
    ├── core/                   # http_client, rate_limiter, rawstore, proxy, errors
    ├── fixtures/               # данные для офлайн-тестов
    ├── sample_export.json      # пример входа для `manual`
    └── tests/run_tests.py

About

Modular TikTok OSINT tool — scrapes descriptions & comments, pluggable collectors, PII minimization, JSONL/Postgres sinks

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages