Получаем число подписчиков YouTube-канала

27 мая 2026 г.

Для Telegram я уже делал похожую заметку. На YouTube задача немного сложнее, но принцип тот же: если нет простого публичного API под конкретную задачу, можно аккуратно распарсить веб-страницу канала.

Цель — сделать функцию и API-эндпоинт, который возвращает число подписчиков YouTube-канала. В расширенном варианте хочется получить еще:

  • ссылку на изображение профиля;
  • название канала;
  • ссылку на канал;
  • описание;
  • количество подписчиков;
  • количество видео;
  • количество просмотров;
  • дату регистрации.

План

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

Если открыть исходник страницы, можно найти JSON-объект . Внутри него есть , где уже лежат нужные поля:

А данные аватарки и названия удобно брать из .

Решение

Ниже пример функции на TypeScript, которая делает запрос к странице , извлекает и возвращает подписчиков и другие метаданные.

import axios from 'axios';

type UnknownRecord = Record<string, unknown>;

function extractInitialData(html: string): UnknownRecord | null {
  const match = html.match(/var ytInitialData\s*=\s*(\{[\s\S]*?\});<\/script>/);
  if (!match?.[1]) return null;

  try {
    return JSON.parse(match[1]) as UnknownRecord;
  } catch {
    return null;
  }
}

function findObjectByKey(root: unknown, key: string): UnknownRecord | null {
  const queue: unknown[] = [root];

  while (queue.length > 0) {
    const current = queue.shift();
    if (!current || typeof current !== 'object') continue;

    const record = current as UnknownRecord;

    if (key in record && record[key] && typeof record[key] === 'object') {
      return record[key] as UnknownRecord;
    }

    if (Array.isArray(current)) queue.push(...current);
    else queue.push(...Object.values(record));
  }

  return null;
}

function getString(value: unknown): string | null {
  return typeof value === 'string' && value.trim() ? value.trim() : null;
}

function parseCount(text: string | null): number | null {
  if (!text) return null;

  const normalized = text.replace(/,/g, '').trim();
  const match = normalized.match(/([\d.]+)\s*([KMB])?/i);
  if (!match?.[1]) return null;

  const base = Number.parseFloat(match[1]);
  if (!Number.isFinite(base)) return null;

  const suffix = (match[2] || '').toUpperCase();
  const multiplier =
    suffix === 'K'
      ? 1_000
      : suffix === 'M'
        ? 1_000_000
        : suffix === 'B'
          ? 1_000_000_000
          : 1;

  return Math.round(base * multiplier);
}

export async function getYoutubeChannelInfo(channel: string) {
  const url = `https://www.youtube.com/${channel}/about?hl=en`;

  const { data: html } = await axios.get<string>(url, {
    headers: {
      'User-Agent':
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122 Safari/537.36',
      'Accept-Language': 'en-US,en;q=0.9',
    },
  });

  const initialData = extractInitialData(html);
  if (!initialData) {
    throw new Error('Failed to parse ytInitialData');
  }

  const about = findObjectByKey(initialData, 'aboutChannelViewModel');
  const metadata = findObjectByKey(initialData, 'channelMetadataRenderer');

  const subscribersText = getString(about?.subscriberCountText);
  const videosText = getString(about?.videoCountText);
  const viewsText = getString(about?.viewCountText);

  return {
    title: getString(metadata?.title),
    description:
      getString(about?.description) || getString(metadata?.description),
    link:
      getString(about?.canonicalChannelUrl) || getString(metadata?.channelUrl),
    subscribersText,
    subscribers: parseCount(subscribersText),
    videosText,
    videos: parseCount(videosText),
    viewsText,
    views: parseCount(viewsText),
    joinedAt:
      typeof about?.joinedDateText === 'string'
        ? about.joinedDateText
        : getString(
            (about?.joinedDateText as UnknownRecord | undefined)?.content
          ),
  };
}

Эта функция не завязана на CSS-селекторы страницы. Вместо этого она читает встроенный JSON, в котором данные более структурированы.

API-эндпоинт

Я обернул эту логику в API-роут:

Он принимает разные форматы входа:

  • полный URL канала
  • channel id

И возвращает JSON с подписчиками и дополнительными метаданными.

Пример использования

Можно проверить в браузере или через curl:

Или сразу протестировать вживую прямо в заметке:

curl 'https://your-domain.com/api/get-youtube-subscribers-count?channel=@talyguryn'

Пример ответа:

{
  "uri": "@talyguryn",
  "link": "http://www.youtube.com/@talyguryn",
  "title": "Мастерская Тали",
  "description": "пишу код и делюсь мыслями",
  "image": "https://yt3.googleusercontent.com/...",
  "updatedAt": "2026-04-18T12:00:00.000Z",
  "subscribers": 52,
  "subscribersText": "52 subscribers",
  "videos": 17,
  "videosText": "17 videos",
  "views": 5995,
  "viewsText": "5,995 views",
  "joinedAt": "Joined Feb 8, 2013"
}

Улучшения

Что можно улучшить дальше:

  • добавить кэширование на 5-10 минут;
  • нормализовать язык ответа (например, в ISO-дату);
  • добавить rate-limit на эндпоинт;
  • отдавать и поля отдельно для удобной отладки;
  • добавить fallback через YouTube Data API v3 (если есть ключ).

Ограничения

Решение неофициальное. Если YouTube изменит структуру , парсер придется обновить.

Также важно не спамить запросами, особенно если эндпоинт публичный.

Финал

Вот так можно получить число подписчиков YouTube-канала и дополнительные данные из блока на странице .

Для небольших внутренних задач и персональных проектов этого часто более чем достаточно.

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