sociallinks.dev

בלוג / מדריך למפתחים

איך לקבל מזהה ערוץ YouTube משם משתמש (ולהפך)

יוצרים משתפים את ערוץ ה-YouTube שלהם כ-@handle, אבל כמעט הכול ב-YouTube Data API מבוסס על מזהה ערוץ. אם שומרים רק שמות משתמש, שינוי שם שובר את הרשומות; אם שומרים רק מזהים, אי אפשר להציג למשתמשים את השם שהם מכירים. המדריך הזה מראה איך להמיר שם משתמש למזהה ערוץ בקריאת API אחת, איך לחזור ממזהים לשמות משתמש 50 בכל פעם, מה לעשות עם כתובות ישנות של /user/ ו-/c/, ואיך להישאר בתוך מכסת ברירת המחדל.

שמות משתמש, מזהי ערוץ ושמות ישנים

אפשר להגיע לערוץ YouTube דרך עד ארבעה סוגי מזהים. רק אחד מהם מובטח כיציב.

מזההדוגמהצורת כתובתהערות
מזהה ערוץUC_x5XG1OV2P6uZZ5FSM9Ttwyoutube.com/channel/UC...המזהה ש-YouTube משתמשת בו כדי לזהות את הערוץ. רגיש לרישיות. השתמשו בו כמפתח הראשי.
שם משתמש (handle)@GoogleDevelopersyoutube.com/@GoogleDevelopersייחודי, לא רגיש לרישיות, והבעלים יכול לשנות אותו ב-YouTube Studio.
שם משתמש ישן (legacy username)GoogleDevelopersyoutube.com/user/GoogleDevelopersממערכת שמות המשתמש הישנה. אפשר להמיר אותו עם המסנן forUsername של ה-API.
כתובת מותאמת ישנהGoogleDevelopersyoutube.com/c/GoogleDevelopersכתובות מותאמות אישית ישנות, ש-YouTube אומרת שהן ממשיכות לעבוד. אין מסנן API שממיר אותן.

שמרו את מזהה הערוץ כמפתח, ואת שם המשתמש כערך תצוגה שאתם מרעננים. שאר המדריך עוסק בדרך ממה שהמשתמש נתן לכם אל המזהה הזה.

הגדרת מפתח API והבנת המכסה

  1. Step 1צרו פרויקט ב-Google Cloud

    ב-Google Cloud console, צרו פרויקט או בחרו פרויקט קיים.

  2. Step 2הפעילו את YouTube Data API v3

    פתחו את APIs and services, מצאו את YouTube Data API v3 בספרייה והפעילו אותו בפרויקט.

  3. Step 3צרו מפתח API

    תחת Credentials, צרו מפתח API. נתוני ערוץ ציבוריים כמו channels.list לפי שם משתמש או מזהה צריכים רק מפתח, לא OAuth. הגבילו את המפתח ל-YouTube Data API.

  4. Step 4השאירו את המפתח מחוץ לקוד

    שימו אותו במשתנה סביבה, למשל YOUTUBE_API_KEY, וקראו אותו בזמן ריצה. הדוגמאות שבהמשך עושות בדיוק את זה.

  5. Step 5בדקו את המכסה

    בקונסול, דף ה-Quotas של ה-API מציג את ההקצאה היומית ואת השימוש.

המספרים שחשובים, מתוך התיעוד של Google:

כלומר מכסת ברירת המחדל מספיקה לכ-10,000 חיפושי שמות משתמש ביום, ועד פי 50 חיפושי מזהים, כי קריאה אחת יכולה להביא 50 ערוצים לפי מזהה באותה יחידה אחת.

משם משתמש למזהה ערוץ עם forHandle

channels.list מקבל מסנן אחד בדיוק. המסנן forHandle מקבל שם משתמש של YouTube, והתיעוד אומר שאפשר להוסיף לערך @ בהתחלה, כך שגם GoogleDevelopers וגם @GoogleDevelopers עובדים. בקשו part=id כשאתם צריכים רק את המזהה; התשובה קטנה יותר והעלות זהה.

cURL

curl -s "https://www.googleapis.com/youtube/v3/channels?part=id&forHandle=@GoogleDevelopers&key=$YOUTUBE_API_KEY"

דוגמת ה-Node שבהמשך עוטפת את הבקשה בפונקציית עזר אחת, listChannels, ששאר המדריך משתמש בה שוב. היא מחזירה רשימה ריקה כשאין התאמה וזורקת שגיאה על בעיות אמיתיות כמו מפתח שגוי או מכסה שנגמרה.

Node.js

const API = "https://www.googleapis.com/youtube/v3/channels";
async function listChannels(params) {
  const key = process.env.YOUTUBE_API_KEY;
  if (!key) throw new Error("Set YOUTUBE_API_KEY");
  const url = new URL(API);
  url.search = new URLSearchParams({ ...params, key }).toString();
  const res = await fetch(url, { signal: AbortSignal.timeout(10_000) });
  const data = await res.json();
  if (!res.ok) {
    const reason = data.error?.errors?.[0]?.reason;
    if (reason === "channelNotFound") return [];
    throw new Error(`YouTube API ${res.status} ${reason ?? ""}: ${data.error?.message}`);
  }
  return data.items ?? []; // no match: items is empty or missing
}

/** "@GoogleDevelopers" or "GoogleDevelopers" -> "UC_x5XG1OV2P6uZZ5FSM9Ttw" or null */
export async function channelIdFromHandle(handle) {
  const items = await listChannels({ part: "id", forHandle: handle.trim() });
  return items[0]?.id ?? null;
}

/** Legacy username from a youtube.com/user/<name> URL -> channel ID or null */
export async function channelIdFromUsername(username) {
  const items = await listChannels({ part: "id", forUsername: username.trim() });
  return items[0]?.id ?? null;
}

/** Any number of channel IDs -> Map(id -> { title, customUrl }) */
export async function channelsById(ids) {
  const unique = [...new Set(ids)];
  const found = new Map();
  for (let i = 0; i < unique.length; i += 50) {
    const batch = unique.slice(i, i + 50);
    const items = await listChannels({ part: "snippet", id: batch.join(","), maxResults: "50" });
    for (const ch of items) {
      found.set(ch.id, { title: ch.snippet.title, customUrl: ch.snippet.customUrl ?? null });
    }
  }
  return found; // IDs missing from the map were not found
}

Python

import os

import requests

API = "https://www.googleapis.com/youtube/v3/channels"


def list_channels(**params):
    key = os.environ.get("YOUTUBE_API_KEY")
    if not key:
        raise RuntimeError("Set YOUTUBE_API_KEY")
    response = requests.get(API, params={**params, "key": key}, timeout=10)
    data = response.json()
    if not response.ok:
        errors = data.get("error", {}).get("errors") or [{}]
        reason = errors[0].get("reason")
        if reason == "channelNotFound":
            return []
        raise RuntimeError(f"YouTube API {response.status_code} {reason}: {data.get('error', {}).get('message')}")
    return data.get("items", [])  # no match: items is empty or missing


def channel_id_from_handle(handle):
    """'@GoogleDevelopers' or 'GoogleDevelopers' -> 'UC_x5XG1OV2P6uZZ5FSM9Ttw' or None"""
    items = list_channels(part="id", forHandle=handle.strip())
    return items[0]["id"] if items else None


def channel_id_from_username(username):
    """Legacy username from a youtube.com/user/<name> URL -> channel ID or None"""
    items = list_channels(part="id", forUsername=username.strip())
    return items[0]["id"] if items else None


def channels_by_id(ids):
    """Any number of channel IDs -> {id: {"title", "customUrl"}}; missing IDs were not found"""
    unique = list(dict.fromkeys(ids))
    found = {}
    for i in range(0, len(unique), 50):
        batch = unique[i:i + 50]
        for ch in list_channels(part="snippet", id=",".join(batch), maxResults=50):
            found[ch["id"]] = {
                "title": ch["snippet"]["title"],
                "customUrl": ch["snippet"].get("customUrl"),
            }
    return found

העבירו שמות משתמש כפי שהמשתמש כתב אותם, אחרי פענוח קידוד אחוזים מהכתובת. שמות משתמש יכולים לכלול אותיות מכתבים רבים, לא רק ASCII, וגם URLSearchParams וגם requests מקודדים את הפרמטר בשבילכם.

שמות משתמש ישנים וכתובות מותאמות /c/

קישורים ישנים בביו של יוצרים ובגיליונות משתמשים לעיתים קרובות באחת מהצורות שקדמו לשמות המשתמש.

ממזהה ערוץ לשם משתמש, 50 בכל פעם

הכיוון ההפוך משתמש במסנן id, שמקבל רשימה של מזהי ערוץ מופרדים בפסיקים. בקשו part=snippet וקראו את snippet.customUrl. הפונקציה channelsById שלמעלה מסירה כפילויות, שולחת את המזהים בחבילות של 50 ומחזירה מפה לפי מזהה. מזהים שלא מופיעים במפה לא נמצאו, נמחקו או לא גלויים ל-API.

לא נמצא, שגיאות ומטמון

חיפוש שלא מוצא כלום אינו שגיאה. ב-forHandle וב-forUsername פשוט אין פריטים בתשובה, אז בדקו אם מערך items ריק או חסר. אלה השגיאות המתועדות של channels.list:

סיבת השגיאהHTTPמתי זה קורהמה לעשות
אין items בתשובת 200200שם המשתמש או שם המשתמש הישן לא קייםהחזירו null; שמרו את ההחטאה במטמון לזמן קצר
channelNotFound404ערוץ בפרמטר id לא נמצאהתייחסו לזה כלא נמצא, כמו שפונקציות העזר שלמעלה עושות
invalidCriteria400יותר ממסנן אחד בבקשהשלחו בדיוק אחד מ-forHandle, forUsername או id
channelForbidden403הערוץ לא תומך בבקשה או שהיא לא מורשית כראוירשמו ביומן ודלגו על הערוץ
quotaExceeded403הפרויקט ניצל את המכסה היומיתעצרו ונסו שוב אחרי חצות לפי Pacific Time, או בקשו מכסה נוספת
badRequest (מפתח API לא תקין)400מפתח חסר או שגויתקנו את ההגדרות; אל תנסו שוב

שמרו תוצאות במטמון כדי לחסוך מכסה וזמן תגובה, אבל כבדו את הכללים של YouTube לגבי שמירת נתונים. YouTube API Services Developer Policies (סעיף III.E.4) אומרות שמותר לשמור נתוני API לא מורשים לא יותר מ-30 ימים קלנדריים, ואחר כך צריך למחוק או לרענן אותם. מטמון עם TTL הרבה מתחת ל-30 יום, לפי שם המשתמש באותיות קטנות, נותן את רוב התועלת:

מטמון ב-Node.js

import { channelIdFromHandle } from "./youtube.mjs";

const TTL_MS = 7 * 24 * 60 * 60 * 1000; // refresh well inside YouTube's 30-day limit
const cache = new Map(); // lowercase handle -> { id, at }

export async function cachedChannelId(handle) {
  const key = handle.trim().replace(/^@/, "").toLowerCase();
  const hit = cache.get(key);
  if (hit && Date.now() - hit.at < TTL_MS) return hit.id;
  const id = await channelIdFromHandle(handle);
  cache.set(key, { id, at: Date.now() }); // caches misses (null) too
  return id;
}

בייצור, שימו את אותו היגיון ב-Redis או בטבלת מסד נתונים עם עמודת expires_at, ותנו להחטאות TTL קצר יותר מאשר לפגיעות, כדי ששם משתמש שנוצר זה עתה ייקלט מהר.

חלופה בלי מפתח API: קריאת דף הערוץ

דפי ערוץ ציבוריים כוללים את מזהה הערוץ ב-HTML שלהם. בקשה ל-https://www.youtube.com/@GoogleDevelopers מחזירה כרגע דף שה-<link rel="canonical"> שלו מצביע על https://www.youtube.com/channel/UC_x5XG1OV2P6uZZ5FSM9Ttw, וכך גם דפי /c/ ו-/user/, ולכן זו הדרך היחידה להמיר שם /c/ בלי לנחש.

Node.js

// Unofficial fallback: reads the public channel page. Fragile, prefer the API.
export async function channelIdFromPage(handle) {
  const url = `https://www.youtube.com/@${encodeURIComponent(handle.replace(/^@/, ""))}`;
  const res = await fetch(url, {
    headers: { "Accept-Language": "en-US" },
    signal: AbortSignal.timeout(10_000),
  });
  if (!res.ok) return null;
  const html = await res.text();
  const match = html.match(/<link rel="canonical" href="https:\/\/www\.youtube\.com\/channel\/(UC[A-Za-z0-9_-]{22})"/);
  return match?.[1] ?? null;
}

Python

import re
from urllib.parse import quote

import requests

CANONICAL = re.compile(r'<link rel="canonical" href="https://www\.youtube\.com/channel/(UC[A-Za-z0-9_-]{22})"')


def channel_id_from_page(handle):
    """Unofficial fallback: reads the public channel page. Fragile, prefer the API."""
    url = "https://www.youtube.com/@" + quote(handle.lstrip("@"), safe="._-")
    response = requests.get(url, headers={"Accept-Language": "en-US"}, timeout=10)
    if not response.ok:
        return None
    match = CANONICAL.search(response.text)
    return match.group(1) if match else None

השתמשו בזה במשורה ורק כשה-API לא יכול לעזור. זה לא רשמי ושביר:

השוואה בין הגישות

גישהקלטעלותיתרונותחסרונות
channels.list עם forHandleשם משתמש, עם או בלי @יחידה אחת לשם משתמשרשמי, התאמה מדויקת, עובד לכל שם משתמששם משתמש אחד לקריאה
channels.list עם forUsernameשם /user/ ישןיחידה אחת לשםרשמי, התאמה מדויקתרק לשמות משתמש ישנים
channels.list עם idעד 50 מזהי ערוץיחידה אחת ל-50 מזהיםרשמי, באצוות, מחזיר כותרת ו-customUrlcustomUrl יכול להיות חסר
search.listטקסט חופשימגבלה נפרדת של 100 קריאות ליום כברירת מחדלמוצא ערוצים לפי שםניחושים מדורגים, לא מדויק; הקצאה קטנה
קישור קנוני בדף הערוץכל כתובת ערוץ, כולל /c/בלי מכסת APIעובד בלי מפתח; ממיר שמות /c/לא רשמי, שביר, עלול להיחסם, עלול להתנגש עם התנאים

לרוב המוצרים ההגדרה הנכונה פשוטה: המירו שמות משתמש עם forHandle, שמרו את מזהה הערוץ, רעננו שמות משתמש באצוות של 50 עם המסנן id, והשאירו את החלופה של קריאת הדף מאחורי דגל למקרים הנדירים של קישור /c/. אם אתם צריכים גם את החשבונות של היוצר בפלטפורמות אחרות, sociallinks.dev הוא API שמקבל כתובת פרופיל אחת של Instagram, TikTok, YouTube או X ומחזיר את החשבונות של היוצר בפלטפורמות אחרות, עם ציון ביטחון והראיות לכל התאמה.

שאלות נפוצות

האם צריך את ה-@ כשמעבירים שם משתמש ל-forHandle?

לא. התיעוד של channels.list אומר שאפשר להוסיף @ לתחילת הערך של forHandle, ונותן גם את GoogleDevelopers וגם את @GoogleDevelopers כדוגמאות.

כמה מכסה צורך חיפוש של שם משתמש?

יחידה אחת. כל קריאה ל-channels.list עולה יחידה אחת, והקצאת ברירת המחדל היא 10,000 יחידות ליום, שמתאפסות בחצות לפי Pacific Time. גם בקשות לא תקינות עולות לפחות יחידה אחת.

אפשר לחפש כמה שמות משתמש בקריאה אחת?

לא. forHandle מקבל שם משתמש יחיד. רק המסנן id מקבל רשימה מופרדת בפסיקים, אז עבדו באצוות בכיוון ההפוך (ממזהים לשמות משתמש) ושמרו במטמון את הכיוון הרגיל.

האם מזהה ערוץ משתנה אי פעם?

מזהה הערוץ הוא המזהה ש-YouTube משתמשת בו כדי לזהות את הערוץ באופן ייחודי. שינוי שם המשתמש או הכותרת לא משנה אותו. בגלל זה הוא המפתח הראשי הנכון. שמות משתמש, כותרות וכתובות מותאמות יכולים כולם להשתנות.

למה snippet.customUrl חוזר ריק?

לא לכל ערוץ יש כתובת מותאמת, כמו שהיסטוריית הגרסאות של YouTube עצמה מציינת. התייחסו לשדה כאופציונלי, והציגו במקומו את כתובת ה-/channel/UC....

כמה זמן אפשר לשמור את התוצאות במטמון?

מדיניות המפתחים של YouTube מגבילה נתוני API שמורים ל-30 ימים קלנדריים לפני שצריך למחוק או לרענן אותם. TTL של שבוע או פחות לפגיעות, וקצר יותר להחטאות, משאיר אתכם הרבה בתוך הגבול.

מצאו את החשבונות של יוצר