В рамках проекта, сосредоточенного на TikTok (скоро будет!), мне нужен был способ получить медиафайлы видео. Самое близкое, что TikTok официально предоставляет, это их oembed
API, но, к сожалению, он включает только основную информацию, такую как title
, thumbnail_url
и т.д. А другие неофициальные API стоят денег или требуют использования python, чего я делать не хочу. Поэтому мне нужен был другой способ получения этой информации.
Использование шаблонов SSR 📦
Поскольку сайт обрабатывает большие объемы трафика, я знал, что у них есть что-то вроде SSR/гидратации на стороне клиента. Обычно это подразумевает передачу данных клиенту в объекте, встроенном в тег <script>
, сокращая количество необходимых запросов со стороны клиента путем прямого заполнения компонентов этими данными. В Next.js, например, это происходит с помощью реквизитов на стороне сервера.
И вот, пожалуйста! Для каждого видео есть удобный блоб, предоставляемый вместе с разметкой страницы. Вы можете убедиться в этом сами, зайдя на страницу любого видео TikTok (https://www.tiktok.com/:user/video/:id
) и посмотрев содержимое тега SIGI_STATE
в консоли:
JSON.parse(document.getElementById("SIGI_STATE").innerText);
Зная это, я создал простой обработчик с использованием deno, чтобы получить этот блоб и вернуть его напрямую.
Enter cheerio & deno 🥣🦕
Если URL видео прикреплен к запросу (через POST
body или params), простой обработчик может получить (scrape) контент для страницы:
const response = await fetch(`https://tiktok.com/${video}`);
const html = await response.text();
Затем, используя cheerio, этот необработанный текст можно превратить в API, подобный jQuery. Это обеспечивает точку входа для получения содержимого сгенерированного сервером блоба внутри тега скрипта:
const $ = cheerio.load(html);
const appContext = $("#SIGI_STATE").text();
Дальше остается только разобрать это как JSON и вернуть соответствующие данные в ответ (ItemModule
). В настоящее время данные хранятся по уникальному идентификатору, Object.keys
поможет устранить это:
const json = JSON.parse(appContext);
const key = Object.keys(json.ItemModule)[0];
const data = json.ItemModule[key];
return new Response(JSON.stringify({ ...data }), {
headers: { 'Content-Type': 'application/json' },
status: 200,
});
Вот и все! Теперь я могу сделать запрос на стороне клиента, чтобы получить актуальные медиафайлы для видео и отобразить их напрямую. Это также позволяет обойти истечение срока действия CDN TikTok, что не позволит сохранить ссылку на это медиа.
Примечание: если сравнить этот результат с результатом одного из неофициальных вариантов API, который я нашел, то данные выглядят идентично. Так что, скорее всего, они делают то же самое (но берут почти $200/мес за неограниченное использование)!
В любом случае, это нестабильно и может сломаться, если они решат переименовать тег скрипта, ограничить скорость и т.д. Но это демонстрирует, как легко создать экземпляр для собственного проекта. И, возможно, при достаточном трафике ботов TikTok создаст публичный API, который сделает это вместо того, чтобы заставлять нас искать! 😇