Loader Transkrypcji YouTube LangChain, Który Przetrwa Produkcję

Każdy samouczek transkrypcji YouTube LangChain pokazuje te same trzy linie: YoutubeLoader.from_youtube_url(), załaduj, gotowe. Potem wdrażasz do pudełka w chmurze i cicho zwraca nic. Poniżej jest loader zwracający właściwe obiekty Document z hostowanego API zamiast — około 20 linii, plus łańcuch podsumowania, aby udowodnić, że to działa end do end.
Dlaczego wbudowany YoutubeLoader umiera na serwerach
YoutubeLoader nie wymaga oficjalnego API. Skruba YouTube wewnętrzne punkty końcowe napisów z maszyny, która go uruchamia. Z domowego IP to w większości działa. Z AWS, GCP, lub adresu Hetzner to się blokuje lub serwuje puste odpowiedzi, ponieważ YouTube traktuje ruch centrum danych jako ruch botów. Napisałem szczegóły w dlaczego YouTube transkrypcja skrobania przerw na chmurze IPs — krótko: niepowodzenie jest środowiskowe, więc przechodzi każdy test lokalny i umiera dokładnie tam, gdzie ma znaczenie.
youtube2text.org robi pobieranie po jego stronie i przekazuje Ci JSON, które zamienia Twój loader w zwykłe wywołanie HTTP.
Loader
import requests
from langchain_core.documents import Document
API_KEY = "yt_your_key_here"
def load_youtube_transcript(url: str, max_chars: int = 150000) -> Document:
resp = requests.get(
"https://youtube2text.org/api/transcribe",
params={"url": url, "maxChars": max_chars},
headers={"x-api-key": API_KEY},
timeout=90,
)
resp.raise_for_status()
r = resp.json()["result"]
return Document(
page_content=r["content"],
metadata={
"videoId": r["videoId"],
"title": r["title"],
"pubDate": r["pubDate"],
"source": url,
},
)
API zwraca videoId, title, pubDate, content, contentSize i flagę truncated. Tekst transkrypcji idzie do page_content, wszystko inne do metadanych — dokładnie kształt, którego oczekują łańcuchy dalsze i magazyny wektorowe.
Brak klucza jeszcze? Weź wspólny klucz demo:
curl https://youtube2text.org/api/demo-key
Jest ograniczony do 5 filmów na miesiąc na IP, co wystarczy do testowania loadera.
Łańcuch podsumowania
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains.combine_documents import create_stuff_documents_chain
llm = ChatOpenAI(model="gpt-4o-mini")
prompt = ChatPromptTemplate.from_template(
"Summarize this video transcript in five bullet points:\n\n{context}"
)
chain = create_stuff_documents_chain(llm, prompt)
doc = load_youtube_transcript("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
print(chain.invoke({"context": [doc]}))
create_stuff_documents_chain wkleja całą transkrypcję w jeden prompt, co działa, ponieważ maxChars ogranicza ładunek do 150 000 znaków domyślnie. W przypadku podcastów o godzinach na modelach małego kontekstu, przekaż niższy maxChars do loadera lub przejdź do wzorca mapy redukcji.
Co obsługiwać w rzeczywistym kodzie
Dwa tryby niepowodzenia są godne sprzeciwu jawnie. Film z wyłączonymi napisami zwraca HTTP 404 z kodem błędu TRANSCRIPT_UNAVAILABLE — brak ponownej próby czy wymiany biblioteki to naprawia, tekst po prostu nie istnieje. A RATE_LIMIT_EXCEEDED (429) zawiera retryAfterSeconds w JSON błędu, więc przeczytaj go i cofnij się zamiast młócenia:
if resp.status_code == 429:
wait = resp.json()["error"].get("retryAfterSeconds", 60)
Jedno szczere ograniczenie: content jest zwykłym tekstem bez znaczników czasowych. To jest w porządku dla podsumowania i pobierania, ale jeśli potrzebujesz linków głębokich "jump to 12:34", będziesz musiał zadowolić się atrybutem poziomu wideo poprzez videoId w metadanych.
Gdzie iść stąd
Te same obiekty Document wpadają bezpośrednio do magazynu wektorowego — przechodzę przez pełny rurociąg w RAG nad całym kanałem YouTube. Jeśli Twój stos to LlamaIndex zamiast LangChain, równoważna konfiguracja jest tak samo krótka.
Gdy klucz demo 5 filmów przestaje być wystarczający, uzyskaj własny klucz — bezpłatny poziom zawarty, płatne plany od 5,99 USD za 50 filmów na miesiąc.