TikTok, один из крупнейших источников публичных данных коротких видео в интернете, и тренды, хештеги и показатели вовлечённости, которые он раскрывает, действительно полезны для маркетинговых исследований, контент-стратегии и отслеживания трендов. При этом это одна из наиболее сложных платформ для программного чтения: страницы рендерятся на стороне клиента с помощью JavaScript, платформа реагирует на автоматизированный трафик, а обычный HTTP-запрос к URL поиска или профиля обычно возвращает почти пустую оболочку без каких-либо видимых в браузере данных.

Это руководство показывает, как парсить публичные данные TikTok с помощью Python так, чтобы это действительно работало, оставаясь строго в рамках публичного и агрегированного контента. Всё здесь ограничено публичными результатами поиска, публичными лентами хештегов и публичными видео профилей: подписями, количеством лайков, комментариев и репостов, URL видео и датами публикации. Это не распространяется на что-либо за логином, закрытые аккаунты или персональные данные отдельных людей. Прочитайте раздел о правовых аспектах в конце, прежде чем применять это к чему-либо реальному, а для производственного использования предпочтите официальный API TikTok.

Что вы создадите

Небольшой Python-скрипт, который принимает публичный URL поиска или хештега TikTok, получает полностью отрисованную страницу через Crawling API с JavaScript-токеном и извлекает несколько публичных агрегированных полей из каждой карточки видео:

  • Caption, публичный текст, отображаемый на карточке видео.
  • Like, comment, and share counts, агрегированные показатели вовлечённости, отображаемые на карточке, а не данные о людях, которые их оставили.
  • Video URL, публичная постоянная ссылка на каждое видео.
  • Posted date, дата загрузки, показанная на карточке.
  • Hashtags, публичные теги, прикреплённые к каждому видео.

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

Почему обычный запрос не работает на TikTok

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

Значит, рабочий скрапер TikTok должен решать в одном запросе две задачи: настоящий браузер, который рендерит страницу, и IP-адрес, который платформа воспринимает как обычного посетителя. Можно собрать это самостоятельно с помощью headless-браузера и пула ротирующихся резидентских прокси, но поддержание этого стека в рабочем состоянии, основная часть работы. Crawling API объединяет оба компонента в одном вызове. Вы отправляете ему URL с JavaScript-токеном, он рендерит страницу за доверенным резидентским IP-адресом и возвращает готовый HTML для парсинга. Более подробно об основах этого подхода см. в нашем руководстве по сканированию JavaScript-сайтов.

Why the JS token

Crawlbase предлагает два типа токенов. Обычный токен получает статичный HTML; JavaScript (JS) токен сначала рендерит страницу в настоящем браузере. TikTok использует рендеринг на стороне клиента, поэтому здесь нужен JS-токен. Обычный токен возвращает ту же оболочку, что и обычный запрос, без ничего полезного для парсинга.

Предварительные требования

Для начала необходимо подготовить несколько вещей. Ни одна не займёт много времени.

Базовые знания Python. Вы должны уметь запускать скрипт и устанавливать пакеты с помощью pip. Если вы новичок в парсинге HTML, наш вводный материал о использовании BeautifulSoup в Python охватывает сторону извлечения данных.

Python 3.8 или новее. Проверьте версию командой python --version и pip --version. Если Python не установлен, установите его с сайта python.org.

Аккаунт Crawlbase и JS-токен. Зарегистрируйтесь, откройте дашборд и скопируйте токен JavaScript (JS). Вы получаете до 20 000 бесплатных запросов, карта не требуется. Обращайтесь с токеном как с паролем: он аутентифицирует ваши запросы, поэтому не добавляйте его в систему контроля версий.

Настройка проекта

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

bash
python --version

python -m venv tiktok_env
source tiktok_env/bin/activate

pip install crawlbase beautifulsoup4

В Windows активируйте окружение командой tiktok_env\Scripts\activate вместо строки с source. Две зависимости выполняют всю работу: crawlbase, официальный клиент для Crawling API, а beautifulsoup4 парсит возвращаемый HTML, позволяя извлекать отдельные поля по селекторам.

Шаг 1: Получение отрисованной страницы

Начните с получения готовой страницы. Импортируйте CrawlingAPI, инициализируйте его вашим JS-токеном и запросите публичный URL поиска или хештега. TikTok загружает контент асинхронно, поэтому передайте ajax_wait и page_wait, чтобы страница стабилизировалась перед захватом. Проверка статуса перед парсингом позволяет сразу замечать ошибки.

python
from crawlbase import CrawlingAPI
import urllib.parse

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

options = {
    "ajax_wait": "true",
    "page_wait": 10000,
    "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
}

def crawl(page_url):
    response = api.get(page_url, options)
    if response["headers"]["cb_status"] == "200":
        return response["body"].decode("utf-8")
    print(f"Request failed. Crawlbase status: {response['headers']['cb_status']}")
    return None

if __name__ == "__main__":
    query = urllib.parse.quote("cooking recipes")
    url = f"https://www.tiktok.com/search/video?q={query}"
    html = crawl(url)
    print(html[:500] if html else "No HTML returned")

Параметры ожидания важны для цели с рендерингом на стороне клиента. ajax_wait указывает API дождаться завершения загрузки асинхронного контента, а page_wait выдерживает фиксированное количество миллисекунд после загрузки, чтобы поздно рендеримые карточки появились перед захватом страницы. Десять секунд, разумная отправная точка для TikTok; увеличьте значение, если карточки возвращаются пустыми. Пример запрашивает тему (кулинарные рецепты) именно потому, что она безличная и публичная. Запустите скрипт, и вы должны увидеть реальную разметку страницы, что подтверждает работу рендеринга ещё до написания первого селектора.

Crawlbase Crawling API

TikTok требует отрисованной страницы за доверенным IP-адресом в одном вызове. Crawling API принимает JS-токен, запускает страницу в настоящем браузере, чтобы ajax_wait и page_wait могли ждать, ротирует резидентские IP-адреса на стороне сервера и отдаёт готовый HTML, избавляя вас от необходимости самостоятельно управлять парком headless-браузеров и пулом прокси. Попробуйте на публичном поисковом запросе на бесплатном уровне.

Шаг 2: Поиск карточек видео в результатах поиска

Получив отрисованный HTML, загрузите его в BeautifulSoup и найдите листинг поиска, контейнер, содержащий все результаты на странице. TikTok помечает ключевые элементы атрибутами data-e2e, которые намного стабильнее его глубоко вложенных и часто переименовываемых CSS-классов. Результаты поиска находятся под селектором div[data-e2e='search_video-item-list'], и каждый прямой дочерний элемент, это одна карточка видео.

python
from bs4 import BeautifulSoup

def find_video_cards(html):
    soup = BeautifulSoup(html, "html.parser")
    return soup.select("div[data-e2e='search_video-item-list'] > div")

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

Шаг 3: Парсинг публичных полей видео

Из каждой карточки извлеките публичные агрегированные поля. Подпись находится под data-e2e='search-card-video-caption', ссылка на видео под data-e2e='search_video-item', дата публикации в элементе с классом, содержащим DivTimeTag, а счётчик вовлечённости под data-e2e='search-card-like-container'. Каждый селектор ниже обёрнут так, что отсутствующий элемент возвращает None, а не завершает выполнение с ошибкой, поскольку TikTok не рендерит каждое поле в каждой карточке.

python
def text_of(card, selector):
    el = card.select_one(selector)
    return el.text.strip() if el else None

def scrape_video_details(card):
    link = card.select_one("div[data-e2e='search_video-item'] a")
    return {
        "caption": text_of(card, "div[data-e2e='search-card-video-caption'] > div > span"),
        "video_url": link["href"].strip() if link and link.has_attr("href") else None,
        "posted_date": text_of(card, "div[class*='DivTimeTag']"),
        "like_count": text_of(card, "div[data-e2e='search-card-like-container'] > strong"),
    }

Здесь извлекаются только агрегированные, не-персональные поля: текст подписи, публичный URL видео, дата публикации и публичное количество лайков. Количество лайков, комментариев и репостов, это числа; люди, стоящие за ними, не принадлежат вам для сбора. Мы не читаем отдельные комментарии и тех, кто поставил лайк видео, и это сдержанность, которая делает работу правомерной.

Selectors drift

TikTok меняет разметку без предупреждения, поэтому данный код опирается на атрибуты data-e2e, а не на хрупкие вложенные классы. Когда поле возвращается как None, заново изучите страницу в инструментах разработчика браузера и обновите селектор. Периодическое обслуживание, норма для любого производственного скрапера, а не признак поломки.

Шаг 4: Парсинг хештегов

Хештеги, публичные теги, описывающие тему видео, что делает их наиболее полезным агрегированным сигналом на странице для трендового анализа. Они находятся в отдельных якорях под data-e2e='search-common-link'. Собирайте их в список для каждой карточки.

python
def scrape_hashtags(card):
    tags = card.select("a[data-e2e='search-common-link'] > strong")
    return {"hashtags": [t.text.strip() for t in tags]}

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

Шаг 5: Сборка всего вместе

Теперь соедините получение данных и парсинг в один рабочий скрипт. Он получает публичную страницу поиска, обходит каждую карточку видео, объединяет поля видео с хештегами и выводит чистый список JSON.

python
from crawlbase import CrawlingAPI
from bs4 import BeautifulSoup
import urllib.parse
import json

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

options = {
    "ajax_wait": "true",
    "page_wait": 10000,
    "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
}

def crawl(page_url):
    response = api.get(page_url, options)
    if response["headers"]["cb_status"] == "200":
        return response["body"].decode("utf-8")
    print(f"Request failed. Crawlbase status: {response['headers']['cb_status']}")
    return None

def text_of(card, selector):
    el = card.select_one(selector)
    return el.text.strip() if el else None

def scrape_video_details(card):
    link = card.select_one("div[data-e2e='search_video-item'] a")
    return {
        "caption": text_of(card, "div[data-e2e='search-card-video-caption'] > div > span"),
        "video_url": link["href"].strip() if link and link.has_attr("href") else None,
        "posted_date": text_of(card, "div[class*='DivTimeTag']"),
        "like_count": text_of(card, "div[data-e2e='search-card-like-container'] > strong"),
    }

def scrape_hashtags(card):
    tags = card.select("a[data-e2e='search-common-link'] > strong")
    return {"hashtags": [t.text.strip() for t in tags]}

def scrape_search(url):
    html = crawl(url)
    if not html:
        return []
    soup = BeautifulSoup(html, "html.parser")
    cards = soup.select("div[data-e2e='search_video-item-list'] > div")

    results = []
    for card in cards:
        video = scrape_video_details(card)
        video.update(scrape_hashtags(card))
        results.append(video)
    return results

def main():
    query = urllib.parse.quote("cooking recipes")
    url = f"https://www.tiktok.com/search/video?q={query}"
    results = scrape_search(url)
    print(json.dumps(results, indent=2, ensure_ascii=False))

if __name__ == "__main__":
    main()

Тот же скрипт работает для публичной ленты хештега: замените URL поиска на URL хештега, например https://www.tiktok.com/tag/cooking, и при необходимости скорректируйте селектор карточек. Форма результата остаётся той же, что и составляет суть обработки одной карточки за раз.

Как выглядит результат

Запустите полный скрипт и получите чистую запись публичных полей для каждого видео, готовую для записи в JSON, CSV или базу данных.

json
[
  {
    "caption": "Crispy potato snacks recipe",
    "video_url": "https://www.tiktok.com/@artofcooking.example/video/7344763014572182789",
    "posted_date": "3-10",
    "like_count": "8.7M",
    "hashtags": ["#potatosnacks", "#snacks", "#foryou", "#fyp"]
  },
  {
    "caption": "Crispy potato bread rolls",
    "video_url": "https://www.tiktok.com/@recipesoftheworld.example/video/7155082128521186587",
    "posted_date": "2022-10-16",
    "like_count": "6.6M",
    "hashtags": ["#breadroll", "#snacks", "#foodie", "#streetfood"]
  }
]

Счётчики приходят в виде строк отображения, например 8.7M, а не целых чисел, поскольку именно так TikTok их рендерит. Если они нужны вам в числовом виде для агрегации, нормализуйте их на небольшом этапе постобработки (раскрывая суффиксы K и M) перед сохранением.

Обработка пагинации

TikTok использует пагинацию на основе прокрутки: новые карточки загружаются по мере прокрутки пользователем вниз, а не по пронумерованным страницам. Crawling API может имитировать эту прокрутку за вас. Добавьте scroll со значением true, а опционально scroll_interval для управления временем ожидания между прокрутками (в миллисекундах). Это загружает больше карточек в HTML перед его возвратом, и один запрос даёт более глубокий набор результатов.

python
options = {
    "ajax_wait": "true",
    "page_wait": 10000,
    "scroll": "true",
    "scroll_interval": 10000,
    "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
}

Устанавливайте достаточно большой scroll_interval. Агрессивная прокрутка на хорошо защищённой цели, самый быстрый способ достичь лимита скорости. Забирайте разумную выборку и останавливайтесь, а не пытайтесь прокрутить всю ленту за один запуск.

Сохранение в CSV

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

python
import csv

def save_to_csv(rows, filename):
    fieldnames = ["caption", "video_url", "posted_date", "like_count", "hashtags"]
    with open(filename, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        for row in rows:
            row = {**row, "hashtags": " ".join(row.get("hashtags", []))}
            writer.writerow(row)

Вызовите save_to_csv(results, "tiktok_data.csv") со списком из основного скрипта. Теперь у вас есть аккуратная таблица публичных метаданных видео для анализа трендов без хранения каких-либо персональных данных.

Как оставаться незаблокированным

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

  • Дозируйте запросы. Бомбардировка страниц в тесном цикле, самый быстрый способ получить ограничение скорости. Добавляйте реальные задержки между запросами и избегайте агрессивного параллелизма.
  • Используйте ротацию. Пул резидентских IP-адресов распределяет запросы по множеству адресов реальных пользователей, не позволяя ни одному из них превысить лимит скорости. Crawling API берёт это на себя; если вы строите собственный стек, именно в эту часть стоит вкладываться.
  • Читайте коды статусов. Запуск, который начинает возвращать проверки или ошибки, сигнализирует о том, что текущая скорость или уровень IP больше недостаточны. Замедляйтесь, а не усиливайте нагрузку.
  • Держите объём низким и цели разнообразными. Агрегированное трендовое исследование не требует сканирования всей истории хештега. Берите нужную выборку и останавливайтесь.

Более подробный план действий см. в руководстве по обходу блокировок при скрапинге. Если вас интересует только публичный текст вовлечённости, а не метаданные видео, наше руководство по парсингу комментариев TikTok охватывает этот аспект, а если вы предпочитаете выбрать готовый инструмент, наша подборка лучших скраперов TikTok сравнивает варианты.

Законно ли парсить TikTok?

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

Честные, ограничительные правила, которым следует придерживаться. Собирайте только публичные агрегированные данные: публичные подписи, публичные количества лайков, комментариев и репостов, публичные URL видео, даты публикации и хештеги, которые любой может видеть без входа в аккаунт. Никогда не парсите закрытые аккаунты, контент за логином, прямые сообщения или списки подписчиков. Не создавайте профили идентифицируемых людей: относитесь к именам пользователей, никнеймам и написанным пользователями комментариям как к персональным данным, агрегируйте там, где это возможно (счётчики, тренды, распределения хештегов), и не публикуйте повторно чужой контент в привязке к его личности. Когда речь идёт о персональных данных, применяются законы о конфиденциальности, такие как GDPR и CCPA: вам нужно законное основание для обработки и необходимо соблюдать запросы на удаление. Это чёткие границы, и данное руководство намеренно остаётся на агрегированной, публичной стороне всех из них.

Для любого реального или коммерческого использования правильным инструментом является официальный API TikTok. TikTok предлагает API для разработчиков для санкционированного доступа к контенту и метрикам с надёжной структурой и условиями, которых можно придерживаться. Эта статья, техническое руководство, узко ограниченное публичными агрегированными данными. Это не одобрение массового сбора персональных данных и не охват чего-либо за логином. Если ваш проект требует большего, чем небольшая выборка публичных полей, официальный API или формальное соглашение о данных, правильный путь, а не более изощрённый скрапер.

Итоги

Ключевые выводы

  • TikTok рендерится на стороне клиента и защищён от ботов. Обычный запрос возвращает пустую оболочку, поэтому необходимо рендерить страницу перед парсингом.
  • Рендеринг и доверенный IP должны быть в одном вызове. Crawling API с JS-токеном делает оба; ajax_wait и page_wait управляют временем ожидания контента, а scroll обрабатывает бесконечную ленту TikTok.
  • Парсите стабильные сигналы. Атрибуты data-e2e TikTok намного надёжнее его часто переименовываемых вложенных классов.
  • Только публичные агрегаты. Извлекайте подписи, количество лайков, комментариев и репостов, URL видео, даты публикации и хештеги; никогда не трогайте закрытый контент, списки подписчиков или профили отдельных людей.
  • Дозируйте, ротируйте и предпочитайте официальный API. Держите объём низким, опирайтесь на резидентскую ротацию и используйте официальный API TikTok для всего реального или коммерческого.

Часто задаваемые вопросы

Почему обычный запрос не возвращает данные с TikTok?

Потому что TikTok рендерит контент поиска, хештегов и профилей на стороне клиента с помощью JavaScript. Исходный HTML, это оболочка, которая заполняется только после запуска скриптов страницы в браузере, поэтому обычный HTTP-запрос возвращает почти пустое тело. Чтобы получить реальные публичные данные, необходимо сначала рендерить страницу, что делает JS-токен Crawling API.

Нужен обычный токен или JS-токен для TikTok?

JS-токен. Обычный токен получает статичный HTML, который на TikTok представляет собой ту же пустую оболочку, что и обычный запрос. JS-токен рендерит страницу в настоящем браузере перед возвратом HTML, поэтому публичные карточки видео присутствуют при парсинге BeautifulSoup.

Какие данные TikTok безопасно парсить?

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

Как обработать бесконечную прокрутку TikTok?

Установите параметр scroll Crawling API в значение true и настройте scroll_interval для управления временем ожидания между прокрутками. API имитирует прокрутку страницы вниз, чтобы больше карточек видео загрузились в HTML перед его возвратом. Устанавливайте достаточно большой интервал и берите разумную выборку, а не пытайтесь прокрутить всю ленту за один запрос.

Следует ли использовать официальный API TikTok или парсить сайт?

Для любого реального, постоянного или коммерческого использования используйте официальный API TikTok. Это санкционированный путь, обеспечивающий надёжную структуру и соответствие условиям TikTok. Парсинг небольшой выборки публичных агрегированных полей описанным здесь способом подходит для лёгкого публичного исследования данных без API-доступа при условии соблюдения условий использования, robots.txt и лимитов скорости.

Как избежать блокировок при парсинге TikTok?

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

Начать создавать

Обходите любой сайт в масштабе, без борьбы с инфраструктурой.

Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.

Самообслуживание · Звонок отдела продаж не требуется · Доступны корпоративные объёмы краулинга