Descubre Nichos y Ideas de Contenido para YouTube
En la creación de contenido en YouTube, es crucial estar siempre un paso adelante. Con ésta herramienta en la que me inspiró Phoenix Sanchez de análisis, podrás maximizar el potencial de tus videos y optimizar tu canal para obtener los mejores resultados. En este artículo, te presentamos dos piezas fundamentales de código que te permitirán realizar análisis avanzados de YouTube de manera sencilla y efectiva.
¿Como encontrar Contenido que pueda posicionar en Youtube?
El buscador de nichos de Youtube se compone de dos partes principales: un backend desarrollado en Flask y un frontend en HTML. Estos componentes trabajan en conjunto para ofrecerte un análisis detallado de videos y canales en YouTube, permitiéndote tomar decisiones informadas para tu estrategia de contenido.
¿Como Instalo el Programa?
Instalación de Dependencias:
Validador de Correos Electrónicos Gratispip install Flask Flask-CORS requests
¿Cómo utilizar el buscador de nichos de oportunidad para Youtube?
- Lo primero que debes tener es el Visual Studio intalado. Una vez lo tengas creas una carpeta y la abres desde Visual Studio.
- Dentro de la carpeta crea los dos archivos que necesitarás ( app.py y buscador.html)
- Una vez crees los archivos copia y pega los códigos que encontrarás abajo y ejecuta app.py.
- Cuando app.py esté en marcha puedes abrir el buscador.html, meter tu API y empezar a buscar!!!
Funcionalidades Clave
- Búsqueda de Videos:
- Realiza búsquedas de videos en YouTube basadas en palabras clave, tipo de video, duración, orden de relevancia, y más.
- Filtra resultados según el rango de fechas de publicación.
- Obtén detalles estadísticos de los videos y sus canales correspondientes.
- Análisis de Oportunidad:
- Calcula una métrica de “oportunidad” para cada video, basada en la relación entre las vistas y los suscriptores del canal.
- Esta métrica ayuda a identificar videos con alto potencial de éxito.
- Sugerencias de Palabras Clave:
- Proporciona sugerencias de palabras clave para mejorar la visibilidad y el alcance de tus videos.
buscador.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Análisis de YouTube</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Análisis de YouTube</h1>
<div id="apiKeySection">
<input type="text" id="api_key" placeholder="Introduce tu API key de YouTube">
<button onclick="saveApiKey()">Guardar API Key</button>
</div>
<input type="text" id="keyword" placeholder="Introduce una palabra o frase">
<select id="type">
<option value="any">Cualquier tipo</option>
<option value="movie">Película</option>
<option value="episode">Episodio</option>
</select>
<select id="duration">
<option value="any">Cualquier duración</option>
<option value="short">Corto (<4 minutos)</option>
<option value="medium">Medio (4-20 minutos)</option>
<option value="long">Largo (>20 minutos)</option>
</select>
<select id="order">
<option value="relevance">Relevancia</option>
<option value="date">Fecha</option>
<option value="rating">Calificación</option>
<option value="viewCount">Vistas</option>
</select>
<select id="dateRange">
<option value="">Cualquier fecha</option>
<option value="hour">Última hora</option>
<option value="today">Hoy</option>
<option value="week">Esta semana</option>
<option value="month">Este mes</option>
<option value="year">Último año</option>
</select>
<select id="maxResults">
<option value="10">10 resultados</option>
<option value="25">25 resultados</option>
<option value="50">50 resultados</option>
</select>
<select id="regionCode">
<option value="">Global</option>
<option value="US">Estados Unidos</option>
<option value="ES">España</option>
<option value="MX">México</option>
<option value="AR">Argentina</option>
<option value="CO">Colombia</option>
<option value="PE">Perú</option>
<option value="CL">Chile</option>
</select>
<button onclick="search()">Buscar</button>
<div id="error"></div>
<div id="results">
<h2>Videos mejor posicionados:</h2>
<div class="results-grid"></div>
</div>
<script>
const API_URL = 'http://localhost:5000';
function saveApiKey() {
const apiKey = document.getElementById('api_key').value;
if (apiKey) {
sessionStorage.setItem('youtubeApiKey', apiKey);
alert('API Key guardada para esta sesión.');
document.getElementById('apiKeySection').style.display = 'none';
} else {
alert('Por favor, introduce una API Key válida.');
}
}
function getApiKey() {
return sessionStorage.getItem('youtubeApiKey') || document.getElementById('api_key').value;
}
function search() {
const apiKey = getApiKey();
const keyword = document.getElementById('keyword').value;
const type = document.getElementById('type').value;
const duration = document.getElementById('duration').value;
const order = document.getElementById('order').value;
const dateRange = document.getElementById('dateRange').value;
const maxResults = document.getElementById('maxResults').value;
const regionCode = document.getElementById('regionCode').value;
if (!apiKey) {
showError('Por favor, introduce tu API key de YouTube.');
return;
}
if (!keyword) {
showError('Por favor, introduce una palabra clave.');
return;
}
let url = `${API_URL}/search?api_key=${encodeURIComponent(apiKey)}&keyword=${encodeURIComponent(keyword)}&type=${type}&duration=${duration}&order=${order}&dateRange=${dateRange}&maxResults=${maxResults}®ionCode=${regionCode}`;
fetch(url)
.then(response => response.json())
.then(data => {
if (data.error) {
throw new Error(data.error);
}
if (data.items && data.items.length > 0) {
displayResults(data.items);
suggestKeywords(apiKey, keyword, regionCode);
} else {
showError('No se encontraron resultados para esta búsqueda.');
}
})
.catch(err => showError('Error en la búsqueda: ' + err.message));
}
function displayResults(videos) {
const resultsGrid = document.querySelector('#results .results-grid');
resultsGrid.innerHTML = ''; // Limpiar resultados anteriores
videos.forEach(video => {
const stats = video.statistics || { views: 'N/A', subscribers: 'N/A', opportunity: 'N/A' };
const opportunityScore = Math.round(parseFloat(stats.opportunity));
const videoItem = document.createElement('div');
videoItem.className = 'video-item';
videoItem.innerHTML = `
<strong>${video.snippet.title}</strong><br>
Canal: ${video.snippet.channelTitle}<br>
<a href="https://www.youtube.com/watch?v=${video.id.videoId}" target="_blank">Ver video</a>
<div class="video-stats">
<div class="video-stat">
<strong>Vistas:</strong> ${stats.views}
</div>
<div class="video-stat">
<strong>Suscriptores:</strong> ${stats.subscribers}
</div>
<div class="video-stat">
<strong>Oportunidad:</strong>
<span class="opportunity-score opportunity-${opportunityScore}">${stats.opportunity}</span>
</div>
</div>
`;
resultsGrid.appendChild(videoItem);
});
}
function suggestKeywords(apiKey, keyword, regionCode) {
fetch(`${API_URL}/suggest?api_key=${encodeURIComponent(apiKey)}&keyword=${encodeURIComponent(keyword)}®ionCode=${regionCode}`)
.then(response => response.json())
.then(data => {
if (data.error) {
throw new Error(data.error);
}
if (data.items && data.items.length > 0) {
const suggestions = data.items.map(item => item.snippet.title);
const resultsDiv = document.getElementById('results');
resultsDiv.innerHTML += '<h2>Sugerencias de palabras clave:</h2>';
resultsDiv.innerHTML += '<ul>' + suggestions.map(s => `<li>${s}</li>`).join('') + '</ul>';
} else {
showError('No se encontraron sugerencias para esta búsqueda.');
}
})
.catch(err => showError('Error al sugerir palabras clave: ' + err.message));
}
function showError(message) {
const errorDiv = document.getElementById('error');
errorDiv.innerHTML = message;
console.error(message);
}
window.onload = function() {
const savedApiKey = sessionStorage.getItem('youtubeApiKey');
if (savedApiKey) {
document.getElementById('api_key').value = savedApiKey;
document.getElementById('apiKeySection').style.display = 'none';
}
};
</script>
</body>
</html>
app.py
from flask import Flask, request, jsonify
from flask_cors import CORS
import requests
from datetime import datetime, timedelta
import math
app = Flask(__name__)
CORS(app)
def get_published_after(date_range):
now = datetime.utcnow()
if date_range == 'hour':
return (now - timedelta(hours=1)).isoformat('T') + 'Z'
elif date_range == 'today':
return now.replace(hour=0, minute=0, second=0, microsecond=0).isoformat('T') + 'Z'
elif date_range == 'week':
return (now - timedelta(weeks=1)).isoformat('T') + 'Z'
elif date_range == 'month':
return (now - timedelta(days=30)).isoformat('T') + 'Z'
elif date_range == 'year':
return (now - timedelta(days=365)).isoformat('T') + 'Z'
else:
return None
def get_video_details(api_key, video_id):
url = f"https://www.googleapis.com/youtube/v3/videos?part=statistics&id={video_id}&key={api_key}"
response = requests.get(url)
data = response.json()
if 'items' in data and len(data['items']) > 0:
return data['items'][0]['statistics']
return None
def get_channel_details(api_key, channel_id):
url = f"https://www.googleapis.com/youtube/v3/channels?part=statistics&id={channel_id}&key={api_key}"
response = requests.get(url)
data = response.json()
if 'items' in data and len(data['items']) > 0:
return data['items'][0]['statistics']
return None
def calculate_opportunity(views, subscribers):
views = int(views)
subscribers = int(subscribers)
views = max(views, 1)
subscribers = max(subscribers, 1)
log_views = math.log10(views)
log_subscribers = math.log10(subscribers)
view_sub_ratio = views / subscribers
log_ratio = math.log10(view_sub_ratio)
opportunity_base = (0.7 * log_views) + (0.3 * log_subscribers)
opportunity_adjusted = opportunity_base * (1 + (log_ratio / 5))
max_opportunity = math.log10(1e10)
opportunity_scaled = (opportunity_adjusted / max_opportunity) * 9 + 1
return round(min(max(opportunity_scaled, 1), 10), 2)
@app.route('/search', methods=['GET'])
def search():
api_key = request.args.get('api_key')
keyword = request.args.get('keyword')
video_type = request.args.get('type', 'any')
duration = request.args.get('duration', 'any')
order = request.args.get('order', 'relevance')
date_range = request.args.get('dateRange', '')
max_results = request.args.get('maxResults', '10')
region_code = request.args.get('regionCode', '')
if not api_key or not keyword:
return jsonify({'error': 'Se requiere una API key y una palabra clave'}), 400
url = f"https://www.googleapis.com/youtube/v3/search?part=snippet&q={keyword}&type=video&key={api_key}&maxResults={max_results}"
if video_type != 'any':
url += f"&videoType={video_type}"
if duration != 'any':
url += f"&videoDuration={duration}"
if order:
url += f"&order={order}"
if region_code:
url += f"®ionCode={region_code}"
published_after = get_published_after(date_range)
if published_after:
url += f"&publishedAfter={published_after}"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
for item in data.get('items', []):
video_id = item['id']['videoId']
channel_id = item['snippet']['channelId']
video_stats = get_video_details(api_key, video_id)
channel_stats = get_channel_details(api_key, channel_id)
if video_stats and channel_stats:
item['statistics'] = {
'views': video_stats.get('viewCount', '0'),
'subscribers': channel_stats.get('subscriberCount', '0'),
'opportunity': calculate_opportunity(video_stats.get('viewCount', '0'), channel_stats.get('subscriberCount', '0'))
}
return jsonify(data)
except requests.RequestException as e:
return jsonify({'error': str(e)}), 500
@app.route('/suggest', methods=['GET'])
def suggest():
api_key = request.args.get('api_key')
keyword = request.args.get('keyword')
region_code = request.args.get('regionCode', '')
if not api_key or not keyword:
return jsonify({'error': 'Se requiere una API key y una palabra clave'}), 400
url = f"https://www.googleapis.com/youtube/v3/search?part=snippet&q={keyword}&type=video&key={api_key}"
if region_code:
url += f"®ionCode={region_code}"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
return jsonify(data)
except requests.RequestException as e:
return jsonify({'error': str(e)}), 500
@app.route('/test', methods=['GET'])
def test():
return jsonify({"message": "Backend is working!"})
if __name__ == '__main__':
app.run(debug=True)
style.css
body {
font-family: Arial, sans-serif;
line-height: 1.6;
padding: 20px;
margin-left: 10%;
margin-right: 10%;
background-color: #2e2c2c;
color: white;
}
h1 {
color: #cc0000;
text-align: center;
}
input, button, select {
margin: 10px 0;
padding: 10px;
width: 100%;
box-sizing: border-box;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
background-color: #cc0000;
color: white;
border: none;
cursor: pointer;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #990000;
}
#results, #error {
margin-top: 20px;
}
#error {
color: #cc0000;
font-weight: bold;
background-color: #ffeeee;
padding: 10px;
border-radius: 4px;
}
.results-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
.video-item {
border: 1px solid #ddd;
padding: 15px;
background-color: white;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
color: black;
border-radius: 5%;
box-shadow: 0px -1px 5px 0px rgba(255,255,255,1);
}
.video-stats {
display: flex;
flex-direction: column;
margin-top: 10px;
background-color: #990000;
padding: 10px;
border-radius: 4px;
}
.video-stat {
margin-bottom: 5px;
text-align: center;
color: white;
}
.opportunity-score {
font-weight: bold;
padding: 5px 10px;
border-radius: 20px;
color: white;
display: inline-block;
}
/* Escala de colores para la puntuación de oportunidad */
.opportunity-1 { background-color: #ff0000; }
.opportunity-2 { background-color: #ff3300; }
.opportunity-3 { background-color: #ff6600; }
.opportunity-4 { background-color: #dba655; }
.opportunity-5 { background-color: #ffcc00; }
.opportunity-6 { background-color: #cccc5b; }
.opportunity-7 { background-color: #9eb63c; }
.opportunity-8 { background-color: #96d635; }
.opportunity-9 { background-color: #63d616; }
.opportunity-10 { background-color: #4cd32a; }
#apiKeySection {
margin-bottom: 20px;
background-color: #e6e6e6;
padding: 15px;
border-radius: 4px;
}
a {
color: #0066cc;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
@media (max-width: 1200px) {
.results-grid {
grid-template-columns: repeat(3, 1fr);
}
}
@media (max-width: 900px) {
.results-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 600px) {
.results-grid {
grid-template-columns: 1fr;
}
}
Con estas herramientas a tu disposición, estarás mejor equipado para triunfar en la competitiva plataforma de YouTube. ¡Comienza hoy mismo a optimizar tu contenido y a descubrir nuevas oportunidades con nuestro sistema de análisis avanzado!
Acortar Url’s con Google Sheets (Script)