Volver al blog

Cómo recopilar datos de YouTube para sistemas RAG: extracción de subtítulos y metadatos con Python

Guía paso a paso para recopilar datos de YouTube para entrenar sistemas RAG: trabajar con API, analizar subtítulos y metadatos, configurar proxies para escalar.

📅6 de marzo de 2026
```html

Los sistemas RAG (Generación Aumentada por Recuperación) requieren datos de calidad para su entrenamiento. YouTube es una enorme fuente de contenido estructurado: videos con subtítulos, metadatos y comentarios. En este artículo, analizaremos cómo recolectar datos de YouTube de manera efectiva para RAG, evitando bloqueos y cumpliendo con los límites de la API.

Qué es RAG y por qué se necesitan datos de YouTube

RAG (Generación Aumentada por Recuperación) es un enfoque para construir sistemas de IA, donde un modelo de lenguaje se complementa con una base de conocimientos. En lugar de depender únicamente de los datos con los que se ha entrenado el modelo, RAG extrae información relevante de una fuente externa y la utiliza para generar respuestas.

YouTube contiene millones de horas de contenido con subtítulos en diferentes idiomas. Esto convierte a la plataforma en una valiosa fuente de datos para sistemas RAG en diversas áreas:

  • Sistemas educativos — conferencias, tutoriales, cursos con marcas de tiempo
  • Documentación técnica — guías en video sobre programación, DevOps, configuración de software
  • Bases de conocimiento médico — conferencias de médicos, análisis de casos clínicos
  • Analítica de negocios — entrevistas con expertos, casos de estudio, análisis de mercados
  • Soporte de productos — reseñas de productos, preguntas frecuentes en formato de video

La ventaja de los datos de YouTube es su estructura: subtítulos con marcas de tiempo, metadatos (categorías, etiquetas), contexto social (comentarios, me gusta). Todo esto ayuda al sistema RAG a entender no solo el contenido, sino también el contexto de la información.

Qué datos de YouTube son útiles para sistemas RAG

Para que un sistema RAG funcione de manera efectiva, es necesario recolectar varios tipos de datos. Cada tipo cumple con sus propias funciones en el proceso de extracción y generación de información.

Subtítulos (Transcripts)

La principal fuente de datos textuales. YouTube proporciona dos tipos de subtítulos:

  • Automáticos — generados por algoritmos de reconocimiento de voz de Google. Disponibles para la mayoría de los videos en inglés y otros idiomas populares. Precisión del 85-95% dependiendo de la calidad del sonido.
  • Manuales — cargados por los autores o la comunidad. Más precisos, a menudo contienen formato y contexto adicional.

Los subtítulos incluyen marcas de tiempo (timestamps), lo que permite vincular el texto con momentos específicos del video. Esto es crítico para crear enlaces precisos a las fuentes en las respuestas de RAG.

Metadatos de video

Los metadatos ayudan al sistema RAG a comprender el contexto y la relevancia de la información:

Tipo de datos Aplicación en RAG
Título y descripción Búsqueda semántica, determinación del tema
Etiquetas y categorías Clasificación de contenido, filtrado
Fecha de publicación Relevancia de la información (importante para temas técnicos)
Duración Evaluación de la profundidad del tema
Estadísticas (vistas, me gusta) Evaluación de la calidad y popularidad de la fuente
Información del canal Determinación de la autoridad de la fuente

Comentarios

Los comentarios contienen contexto adicional: preguntas de los espectadores, aclaraciones de los autores, discusiones. Para los sistemas RAG, esto es valioso, ya que:

  • Los comentarios a menudo contienen preguntas frecuentes sobre el tema del video
  • Los autores pueden publicar correcciones y adiciones
  • Las discusiones revelan diferentes puntos de vista sobre el problema

Trabajo con YouTube Data API v3: configuración y límites

YouTube Data API v3 es la forma oficial de obtener datos. Proporciona acceso a metadatos, estadísticas y comentarios. Los subtítulos se obtienen a través de métodos separados.

Obtención de la clave API

Para trabajar con la API se necesita una clave de Google Cloud Console:

  1. Visite console.cloud.google.com
  2. Crear un nuevo proyecto o seleccionar uno existente
  3. Activar YouTube Data API v3 en la sección "APIs y Servicios"
  4. Crear credenciales (Credentials) → clave API
  5. Copie la clave: la necesitará para todas las solicitudes

Límites y cuotas

YouTube API utiliza un sistema de cuotas. Cada solicitud "cuesta" una cierta cantidad de unidades:

Operación Costo en cuotas
Búsqueda de videos (search.list) 100 unidades
Obtención de datos de video (videos.list) 1 unidad
Obtención de comentarios (commentThreads.list) 1 unidad

El límite diario por defecto es de 10,000 unidades. Esto equivale a aproximadamente 100 solicitudes de búsqueda o 10,000 solicitudes de metadatos. Para aumentar la cuota, es necesario enviar una solicitud a Google.

Ejemplo básico de trabajo con la API

import requests

API_KEY = 'tu_clave_api'
BASE_URL = 'https://www.googleapis.com/youtube/v3'

# Búsqueda de videos por consulta
def search_videos(query, max_results=10):
    url = f'{BASE_URL}/search'
    params = {
        'part': 'snippet',
        'q': query,
        'type': 'video',
        'maxResults': max_results,
        'key': API_KEY
    }
    
    response = requests.get(url, params=params)
    return response.json()

# Obtención de metadatos de video
def get_video_details(video_id):
    url = f'{BASE_URL}/videos'
    params = {
        'part': 'snippet,contentDetails,statistics',
        'id': video_id,
        'key': API_KEY
    }
    
    response = requests.get(url, params=params)
    return response.json()

# Ejemplo de uso
results = search_videos('tutorial de aprendizaje automático', max_results=5)
for item in results.get('items', []):
    video_id = item['id']['videoId']
    title = item['snippet']['title']
    print(f'ID: {video_id}, Título: {title}')
    
    # Obtenemos información detallada
    details = get_video_details(video_id)
    stats = details['items'][0]['statistics']
    print(f"Vistas: {stats.get('viewCount')}, Me gusta: {stats.get('likeCount')}")

Análisis de subtítulos de video: automáticos y manuales

YouTube Data API v3 no proporciona acceso directo a los subtítulos. Para obtenerlos, se utilizan métodos alternativos.

Uso de la biblioteca youtube-transcript-api

La forma más sencilla es la biblioteca youtube-transcript-api para Python. Extrae subtítulos directamente, sin necesidad de clave API:

from youtube_transcript_api import YouTubeTranscriptApi

# Obtención de subtítulos
video_id = 'dQw4w9WgXcQ'

try:
    # Intentar obtener subtítulos en ruso, si no hay, en inglés
    transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=['ru', 'en'])
    
    # Mostrar subtítulos con marcas de tiempo
    for entry in transcript:
        start_time = entry['start']
        duration = entry['duration']
        text = entry['text']
        print(f"[{start_time:.2f}s] {text}")
        
except Exception as e:
    print(f"Error al obtener subtítulos: {e}")

# Obtener lista de idiomas disponibles
transcript_list = YouTubeTranscriptApi.list_transcripts(video_id)
for transcript in transcript_list:
    print(f"Idioma: {transcript.language}, Automáticos: {transcript.is_generated}")

La biblioteca determina automáticamente los subtítulos disponibles y puede traducirlos a otros idiomas (si YouTube proporciona esa opción).

Procesamiento de marcas de tiempo para RAG

Para los sistemas RAG, es importante mantener la conexión entre el texto y las marcas de tiempo. Esto permite crear enlaces precisos a las fuentes:

def format_timestamp(seconds):
    """Conversión de segundos a formato MM:SS"""
    minutes = int(seconds // 60)
    secs = int(seconds % 60)
    return f"{minutes:02d}:{secs:02d}"

def create_chunks_with_timestamps(transcript, chunk_size=500):
    """División de subtítulos en fragmentos manteniendo las marcas de tiempo"""
    chunks = []
    current_chunk = ""
    chunk_start_time = 0
    
    for i, entry in enumerate(transcript):
        if len(current_chunk) == 0:
            chunk_start_time = entry['start']
        
        current_chunk += entry['text'] + " "
        
        # Si se alcanza el tamaño deseado o el final de los subtítulos
        if len(current_chunk) >= chunk_size or i == len(transcript) - 1:
            chunks.append({
                'text': current_chunk.strip(),
                'start_time': chunk_start_time,
                'timestamp': format_timestamp(chunk_start_time),
                'video_id': video_id
            })
            current_chunk = ""
    
    return chunks

# Uso
transcript = YouTubeTranscriptApi.get_transcript(video_id)
chunks = create_chunks_with_timestamps(transcript)

for chunk in chunks[:3]:  # Primeros 3 fragmentos
    print(f"[{chunk['timestamp']}] {chunk['text'][:100]}...")

Recolección de metadatos: títulos, descripciones, etiquetas

Los metadatos enriquecen el contexto para el sistema RAG. Aquí hay un ejemplo completo de recolección de todos los datos necesarios:

import requests
from datetime import datetime

def collect_video_metadata(video_id, api_key):
    """Recolección de metadatos completos de video"""
    url = f'https://www.googleapis.com/youtube/v3/videos'
    params = {
        'part': 'snippet,contentDetails,statistics,topicDetails',
        'id': video_id,
        'key': api_key
    }
    
    response = requests.get(url, params=params)
    data = response.json()
    
    if 'items' not in data or len(data['items']) == 0:
        return None
    
    item = data['items'][0]
    snippet = item['snippet']
    stats = item.get('statistics', {})
    content = item.get('contentDetails', {})
    
    metadata = {
        'video_id': video_id,
        'title': snippet['title'],
        'description': snippet['description'],
        'channel_title': snippet['channelTitle'],
        'channel_id': snippet['channelId'],
        'published_at': snippet['publishedAt'],
        'tags': snippet.get('tags', []),
        'category_id': snippet.get('categoryId'),
        'duration': content.get('duration'),
        'view_count': int(stats.get('viewCount', 0)),
        'like_count': int(stats.get('likeCount', 0)),
        'comment_count': int(stats.get('commentCount', 0)),
        'topics': item.get('topicDetails', {}).get('topicCategories', [])
    }
    
    return metadata

# Ejemplo de uso
metadata = collect_video_metadata('dQw4w9WgXcQ', API_KEY)
print(f"Título: {metadata['title']}")
print(f"Canal: {metadata['channel_title']}")
print(f"Vistas: {metadata['view_count']:,}")
print(f"Etiquetas: {', '.join(metadata['tags'][:5])}")

Determinación de la relevancia del contenido

Para temas técnicos, la frescura de la información es importante. Agreguemos una función para evaluar la relevancia:

from datetime import datetime, timedelta

def calculate_content_freshness(published_date_str):
    """Evaluación de la relevancia del contenido"""
    published_date = datetime.fromisoformat(published_date_str.replace('Z', '+00:00'))
    age_days = (datetime.now(published_date.tzinfo) - published_date).days
    
    if age_days < 30:
        return 'muy_fresco'
    elif age_days < 180:
        return 'fresco'
    elif age_days < 365:
        return 'moderado'
    else:
        return 'viejo'

def calculate_quality_score(metadata):
    """Cálculo de la puntuación de calidad de la fuente"""
    score = 0
    
    # Popularidad
    views = metadata['view_count']
    if views > 100000:
        score += 3
    elif views > 10000:
        score += 2
    elif views > 1000:
        score += 1
    
    # Engagement (me gusta en relación a las vistas)
    if views > 0:
        like_ratio = metadata['like_count'] / views
        if like_ratio > 0.05:
            score += 2
        elif like_ratio > 0.02:
            score += 1
    
    # Relevancia
    freshness = calculate_content_freshness(metadata['published_at'])
    if freshness == 'muy_fresco':
        score += 2
    elif freshness == 'fresco':
        score += 1
    
    return score

# Uso
metadata = collect_video_metadata('dQw4w9WgXcQ', API_KEY)
quality = calculate_quality_score(metadata)
freshness = calculate_content_freshness(metadata['published_at'])
print(f"Puntuación de calidad: {quality}/7")
print(f"Relevancia: {freshness}")

Análisis de comentarios para análisis contextual

Los comentarios pueden contener información valiosa: correcciones de errores en el video, recursos adicionales, preguntas frecuentes. Para los sistemas RAG, esto es contexto adicional.

def get_video_comments(video_id, api_key, max_results=100):
    """Obtención de comentarios de video"""
    url = 'https://www.googleapis.com/youtube/v3/commentThreads'
    comments = []
    next_page_token = None
    
    while len(comments) < max_results:
        params = {
            'part': 'snippet',
            'videoId': video_id,
            'maxResults': min(100, max_results - len(comments)),
            'order': 'relevance',  # Ordenar por relevancia
            'key': api_key
        }
        
        if next_page_token:
            params['pageToken'] = next_page_token
        
        response = requests.get(url, params=params)
        data = response.json()
        
        if 'items' not in data:
            break
        
        for item in data['items']:
            top_comment = item['snippet']['topLevelComment']['snippet']
            comments.append({
                'author': top_comment['authorDisplayName'],
                'text': top_comment['textDisplay'],
                'like_count': top_comment['likeCount'],
                'published_at': top_comment['publishedAt'],
                'reply_count': item['snippet']['totalReplyCount']
            })
        
        next_page_token = data.get('nextPageToken')
        if not next_page_token:
            break
    
    return comments

def filter_valuable_comments(comments, min_likes=5):
    """Filtrado de comentarios valiosos"""
    valuable = []
    
    for comment in comments:
        # Criterios de valor:
        # 1. Muchos me gusta (popularidad)
        # 2. Hay respuestas (provocó discusión)
        # 3. Texto largo (comentario detallado)
        
        if (comment['like_count'] >= min_likes or 
            comment['reply_count'] > 0 or 
            len(comment['text']) > 200):
            valuable.append(comment)
    
    return valuable

# Uso
comments = get_video_comments('dQw4w9WgXcQ', API_KEY, max_results=50)
valuable_comments = filter_valuable_comments(comments)

print(f"Total de comentarios: {len(comments)}")
print(f"Comentarios valiosos: {len(valuable_comments)}")

for comment in valuable_comments[:3]:
    print(f"\n[{comment['like_count']} me gusta] {comment['author']}:")
    print(comment['text'][:200])

Uso de proxies para escalar la recolección de datos

Al recolectar datos a gran escala, surgen dos problemas: los límites de la API de YouTube (10,000 cuotas por día) y bloqueos al analizar subtítulos. Los proxies ayudan a resolver ambas tareas.

Cuándo se necesitan proxies para el análisis de YouTube

  • Exceso de cuotas de API — utilizando varias claves API a través de diferentes IP, se puede aumentar el límite diario
  • Análisis de subtítulos evitando la API — la biblioteca youtube-transcript-api realiza solicitudes directas, que pueden ser bloqueadas a alta frecuencia
  • Recolección de datos de diferentes regiones — algunos videos solo están disponibles en ciertos países
  • Recolección paralela — distribución de la carga en varias IP para acelerar el proceso

Elección del tipo de proxy

Tipo de proxy Ventajas Cuándo usar
Centro de datos Alta velocidad, bajo costo Trabajo con API, volúmenes pequeños
Residenciales Bajo riesgo de bloqueos, IP reales Análisis masivo de subtítulos, eludir restricciones
Móviles Máxima confianza, bloqueos raros Recolección de datos de aplicaciones móviles, tareas críticas

Para la mayoría de las tareas de los sistemas RAG, son adecuados los proxies residenciales — ofrecen un equilibrio entre costo y confiabilidad al analizar a gran escala.

Configuración de proxies en el código

import requests
from youtube_transcript_api import YouTubeTranscriptApi
from youtube_transcript_api._api import TranscriptListFetcher

# Configuración de proxy
PROXY = {
    'http': 'http://usuario:contraseña@servidor-proxy:puerto',
    'https': 'http://usuario:contraseña@servidor-proxy:puerto'
}

# Para trabajar con la API a través de proxy
def get_video_details_with_proxy(video_id, api_key, proxy):
    url = f'https://www.googleapis.com/youtube/v3/videos'
    params = {
        'part': 'snippet,statistics',
        'id': video_id,
        'key': api_key
    }
    
    response = requests.get(url, params=params, proxies=proxy, timeout=10)
    return response.json()

# Para analizar subtítulos a través de proxy
class ProxiedTranscriptApi:
    def __init__(self, proxy):
        self.proxy = proxy
    
    def get_transcript(self, video_id, languages=['en']):
        # Crear una sesión personalizada con proxy
        session = requests.Session()
        session.proxies = self.proxy
        
        # Usar la sesión para las solicitudes
        fetcher = TranscriptListFetcher(session)
        transcript_list = fetcher.fetch(video_id)
        
        # Obtener el idioma deseado
        for lang in languages:
            try:
                transcript = transcript_list.find_transcript([lang])
                return transcript.fetch()
            except:
                continue
        
        raise Exception(f"Subtítulos no encontrados para los idiomas: {languages}")

# Uso
api = ProxiedTranscriptApi(PROXY)
transcript = api.get_transcript('dQw4w9WgXcQ', languages=['ru', 'en'])
print(f"Se obtuvieron {len(transcript)} segmentos de subtítulos")

Rotación de proxies para escalabilidad

Al recolectar datos de miles de videos, es importante distribuir la carga entre varios proxies:

import random
import time

class ProxyRotator:
    def __init__(self, proxy_list):
        self.proxies = proxy_list
        self.current_index = 0
    
    def get_next_proxy(self):
        """Rotación secuencial"""
        proxy = self.proxies[self.current_index]
        self.current_index = (self.current_index + 1) % len(self.proxies)
        return proxy
    
    def get_random_proxy(self):
        """Rotación aleatoria"""
        return random.choice(self.proxies)

# Lista de proxies
PROXY_LIST = [
    {'http': 'http://usuario:contraseña@proxy1:puerto', 'https': 'http://usuario:contraseña@proxy1:puerto'},
    {'http': 'http://usuario:contraseña@proxy2:puerto', 'https': 'http://usuario:contraseña@proxy2:puerto'},
    {'http': 'http://usuario:contraseña@proxy3:puerto', 'https': 'http://usuario:contraseña@proxy3:puerto'},
]

rotator = ProxyRotator(PROXY_LIST)

def collect_data_with_rotation(video_ids):
    results = []
    
    for video_id in video_ids:
        proxy = rotator.get_next_proxy()
        
        try:
            # Obtener metadatos
            metadata = get_video_details_with_proxy(video_id, API_KEY, proxy)
            
            # Obtener subtítulos
            api = ProxiedTranscriptApi(proxy)
            transcript = api.get_transcript(video_id)
            
            results.append({
                'video_id': video_id,
                'metadata': metadata,
                'transcript': transcript
            })
            
            # Retraso entre solicitudes
            time.sleep(1)
            
        except Exception as e:
            print(f"Error para {video_id}: {e}")
            continue
    
    return results

# Uso
video_ids = ['video1', 'video2', 'video3', 'video4', 'video5']
data = collect_data_with_rotation(video_ids)
print(f"Se recolectaron datos para {len(data)} videos")

Procesamiento y preparación de datos para RAG

Después de recolectar los datos, es necesario procesarlos y estructurarlos para el funcionamiento efectivo de los sistemas RAG.

Creación de embeddings vectoriales

Los sistemas RAG utilizan búsqueda vectorial para encontrar fragmentos relevantes. Es necesario convertir el texto en embeddings:

from sentence_transformers import SentenceTransformer
import numpy as np

# Carga del modelo para crear embeddings
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')

def create_embeddings_from_transcript(transcript_chunks):
    """Creación de embeddings para fragmentos de subtítulos"""
    embeddings = []
    
    for chunk in transcript_chunks:
        # Combinamos el texto con los metadatos para mejor contexto
        text_with_context = f"{chunk['title']} | {chunk['text']}"
        
        # Creamos el embedding
        embedding = model.encode(text_with_context)
        
        embeddings.append({
            'video_id': chunk['video_id'],
            'timestamp': chunk['timestamp'],
            'text': chunk['text'],
            'embedding': embedding.tolist(),
            'metadata': {
                'title': chunk['title'],
                'channel': chunk['channel'],
                'views': chunk['views']
            }
        })
    
    return embeddings

# Preparación de datos
def prepare_rag_data(video_data):
    """Preparación de todos los datos para RAG"""
    all_chunks = []
    
    for video in video_data:
        metadata = video['metadata']
        transcript = video['transcript']
        
        # Dividimos los subtítulos en fragmentos
        chunks = create_chunks_with_timestamps(transcript)
        
        # Agregamos metadatos a cada fragmento
        for chunk in chunks:
            chunk['title'] = metadata['title']
            chunk['channel'] = metadata['channel_title']
            chunk['views'] = metadata['view_count']
            all_chunks.append(chunk)
    
    # Creamos embeddings
    embeddings = create_embeddings_from_transcript(all_chunks)
    
    return embeddings

# Uso
rag_data = prepare_rag_data(collected_videos)
print(f"Preparados {len(rag_data)} fragmentos para RAG")

Almacenamiento en base de datos vectorial

Para una búsqueda efectiva, los embeddings se almacenan en bases de datos especializadas. Opciones populares: Pinecone, Weaviate, Qdrant, ChromaDB.

import chromadb
from chromadb.config import Settings

# Inicialización de ChromaDB (base de datos vectorial local)
client = chromadb.Client(Settings(
    chroma_db_impl="duckdb+parquet",
    persist_directory="./youtube_rag_db"
))

# Creación de colección
collection = client.create_collection(
    name="youtube_transcripts",
    metadata={"description": "Transcripciones de videos de YouTube para RAG"}
)

def store_in_vector_db(embeddings_data, collection):
    """Almacenamiento de embeddings en base de datos vectorial"""
    
    ids = []
    embeddings = []
    documents = []
    metadatas = []
    
    for i, item in enumerate(embeddings_data):
        ids.append(f"{item['video_id']}_{i}")
        embeddings.append(item['embedding'])
        documents.append(item['text'])
        metadatas.append({
            'video_id': item['video_id'],
            'timestamp': item['timestamp'],
            'title': item['metadata']['title'],
            'channel': item['metadata']['channel'],
            'views': str(item['metadata']['views']),
            'youtube_url': f"https://youtube.com/watch?v={item['video_id']}&t={int(float(item['timestamp'].split(':')[0])*60 + float(item['timestamp'].split(':')[1]))}s"
        })
    
    # Adición a la colección
    collection.add(
        ids=ids,
        embeddings=embeddings,
        documents=documents,
        metadatas=metadatas
    )
    
    print(f"Almacenados {len(ids)} embeddings en la base de datos vectorial")

# Uso
store_in_vector_db(rag_data, collection)

Búsqueda y generación de respuestas

El paso final es implementar la búsqueda RAG y la generación de respuestas:

def search_youtube_knowledge(query, collection, model, top_k=3):
    """Búsqueda de fragmentos relevantes de YouTube"""
    
    # Creamos el embedding de la consulta
    query_embedding = model.encode(query).tolist()
    
    # Búsqueda en la base de datos vectorial
    results = collection.query(
        query_embeddings=[query_embedding],
        n_results=top_k
    )
    
    # Formateo de resultados
    sources = []
    for i in range(len(results['ids'][0])):
        sources.append({
            'text': results['documents'][0][i],
            'metadata': results['metadatas'][0][i],
            'distance': results['distances'][0][i] if 'distances' in results else None
        })
    
    return sources

def generate_rag_answer(query, sources, llm_api_key):
    """Generación de respuesta basada en las fuentes encontradas"""
    
    # Formamos el contexto a partir de las fuentes encontradas
    context = "\n\n".join([
        f"Fuente: {s['metadata']['title']} ({s['metadata']['timestamp']})\n{s['text']}"
        for s in sources
    ])
    
    # Prompt para LLM
    prompt = f"""Basado en los siguientes fragmentos de videos de YouTube, responde a la pregunta del usuario.
Asegúrate de indicar las fuentes con marcas de tiempo.

Contexto:
{context}

Pregunta: {query}

Respuesta:"""
    
    # Aquí se llamaría a tu LLM (OpenAI, Claude, modelo local)
    # Ejemplo con OpenAI:
    # response = openai.ChatCompletion.create(
    #     model="gpt-4",
    #     messages=[{"role": "user", "content": prompt}]
    # )
    # answer = response.choices[0].message.content
    
    # Para el ejemplo, retornamos el prompt
    return {
        'answer': 'Aquí estará la respuesta de LLM',
        'sources': sources
    }

# Uso
query = "¿Cómo configurar un proxy en Python?"
sources = search_youtube_knowledge(query, collection, model, top_k=3)

print("Fuentes encontradas:")
for source in sources:
    print(f"\n{source['metadata']['title']}")
    print(f"Tiempo: {source['metadata']['timestamp']}")
    print(f"Enlace: {source['metadata']['youtube_url']}")
    print(f"Texto: {source['text'][:200]}...")

Optimización de la calidad RAG

Algunos consejos para mejorar la calidad del sistema RAG en datos de YouTube:

  • Filtra contenido de baja calidad — utiliza métricas de vistas, me gusta, antigüedad de publicación
  • Mantén el contexto — agrega el título del video y del canal a cada fragmento de texto
  • Optimiza el tamaño de los fragmentos — para contenido técnico, 300-500 palabras es óptimo
  • Utiliza metadatos para clasificar — contenido más fresco y popular puede tener prioridad
  • Agrega comentarios — a menudo contienen aclaraciones importantes y preguntas frecuentes
  • Verifica la disponibilidad del video — algunos videos pueden ser eliminados o hacerse privados

Consejo: Para la recolección masiva de datos de YouTube, se recomienda utilizar una combinación de la API oficial (para metadatos) y el análisis a través de proxies (para subtítulos). Esto permite eludir los límites y obtener la máxima información.

Conclusión

La recolección de datos de YouTube para sistemas RAG es un proceso de múltiples etapas que incluye trabajar con la API, analizar subtítulos, procesar metadatos y crear embeddings vectoriales. Puntos clave:

  • YouTube Data API v3 proporciona metadatos, estadísticas y comentarios con un límite de 10,000 cuotas por día
  • Los subtítulos se analizan a través de la biblioteca youtube-transcript-api o solicitudes directas
  • Las marcas de tiempo son críticas para crear enlaces precisos a las fuentes
  • Los metadatos (vistas, me gusta, fecha) ayudan a evaluar la calidad y relevancia del contenido
  • Los comentarios añaden contexto y a menudo contienen preguntas frecuentes
  • Los proxies son necesarios para escalar y eludir límites
  • Los embeddings vectoriales y las bases de datos especializadas permiten una búsqueda semántica rápida

Con la configuración adecuada del proceso, se pueden recolectar decenas de miles de fragmentos de contenido de calidad al día, creando una poderosa base de conocimientos para sistemas RAG en cualquier área temática.

Si planeas una recolección masiva de datos de YouTube eludiendo límites y bloqueos, te recomendamos utilizar proxies residenciales — ofrecen estabilidad al analizar miles de videos y minimizan el riesgo de bloqueos por parte de YouTube.

```