Escribir contenido optimizado para SEO sin perder calidad y relevancia para el usuario es un reto constante que implica bastante research y análisis. Adicionalmente cuando se lo pedimos a una IA los contenidos suelen ser escuetos y de poca relevancia además de clasificar directamente como contenido generado 100% por inteligencia artificial. No pretendo exponer aqui una herramienta para posicionar en primera página, no debería simplificarse a una herrmienta milagrosa porque no solo depende de tu contenido. Sin embargo, hay atajos que nos permiten ahorrar tiempo en análisis y research para generarlo, equipos de contenidos pueden beneficiarse y ahorrar un monton de trabajo con un script como el que les traigo hoy:
Utiliza la API de Gemini para analizar datos provenientes directamente de la SERP, generar una guia detallada para escritores y redactarla en un proceso iterativo con el fin de conseguir un artículo extenso y curado. Algo similar a como podría funcionar una lógica de agentes pero solo trabajando con este LLM.
¿Qué hace este script para generar contenidos con Gemini?
- Extrae los 10 primeros resultados de Google para una keyword
- Scraping de los textos de estos primeros resultados
- Análisis de SEO semántico con Gemini Flash
- Desarrollo de una guia exhaustiva para un escritor en formato markdown
- Desarrollo de cada sección de la guia generada
- Edición y revisión de los contenidos creados
- Publicación automática en WordPress.
Desarrollo del script que genera contenido SEO
Nuestro script se estructura en varias funciones que trabajan en conjunto para generar una guía SEO para escritores, un esquema de contenidos, y el desarrollo de secciones específicas de un artículo long form optimizado para SEO. A continuación, detallo el propósito de cada una de las funciones clave del script:
Lo primero siempre, instalar dependencias:
import os
import requests
import pandas as pd
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())
#importar el modelo GEMINI con el que queremos trabajar
import google.generativeai as genai
# Configurar la API de Gemini (debes generar una en Google Gemini API key y definirla en un archivo .env dentro de tu proyecto)
genai.configure(api_key=os.environ['GEMINI_API_KEY'])
# Cargar el modelo Gemini 1.5 Flash
model = genai.GenerativeModel("gemini-1.5-flash") # ,generation_config=generation_config -- Avtivar para config temp y tokens)
- Extracción de los primeros 10 resultados de Google: Creamos un Custom Search en Google Cloud desde la API de custom search para obtener un buscador personal. Podemos configurarlo para busquedas globales o para buscar en determinados sitios. No voy a profundizar en este apartado porque seria externderme demasiado pero les dejo este video donde se explica con claridad.
#EXTRAE SOLO 10 RESULTADOS
query = "la query de base que definas"
country = "ES" (el país de los resultados de busqueda que pretendas)
def google_custom_search_df(query, country):
API_KEY = os.getenv('API_KEY')
API_CUSTOM_SEARCH_ID = os.getenv('API_CUSTOM_SEARCH_ID')
if not API_KEY or not API_CUSTOM_SEARCH_ID:
print("Error: API Key o ID de búsqueda no están cargados correctamente")
return pd.DataFrame()
# URL request para obtener solo los primeros 10 resultados
url = f"https://www.googleapis.com/customsearch/v1?key={API_KEY}&cx={API_CUSTOM_SEARCH_ID}&q={query}&start=1&gl={country}"
response = requests.get(url)
if response.status_code != 200:
print(f"Error en la petición: {response.status_code}")
return pd.DataFrame()
data = response.json()
# Extraer los primeros 10 resultados
items = data.get("items", [])
if not items:
print("No se encontraron resultados")
return pd.DataFrame()
# Crear un DataFrame con los resultados de la búsqueda
df = pd.DataFrame(items, columns=['title', 'link'])
return df
# Llamada a la función para obtener solo los primeros 10 resultados
df = google_custom_search_df(query, country)
print(df)
- Scraping de los artículos de los primeros resultados: Utilizamos dos librerías de python para scrapear los artículos, si una falla la otra actúa.
#-------------------SCRAPING del texto de los articulos de las 10 primeras posiciones de Google SERP
#Extraer el texto de los articulos con la libreria newspapper y, en su defecto, con beautiful soup
from bs4 import BeautifulSoup
from newspaper import Article
def scrape_article(url):
try:
article = Article(url)
article.download()
article.parse()
if not article.text:
# Si Newspaper no pudo obtener el texto, intenta con BeautifulSoup
page = requests.get(url)
soup = BeautifulSoup(page.content, 'html.parser')
# Busca contenido en etiquetas <p> o <div>
article_text = ' '.join([p.get_text() for p in soup.find_all(['p', 'div'])])
return article_text
return article.text
except Exception as e:
print(f"Error al scrapear el artículo: {str(e)}")
return ""
def scrape_articles_in_dataframe(df):
scraped_texts = [] # Aquí almacenaremos el texto de los artículos
for url in df['link']:
scraped_text = scrape_article(url)
scraped_texts.append(scraped_text)
# Agregamos los textos como una nueva columna en el DataFrame
df['scraped_text'] = scraped_texts
return df
# Llama a la función scraping de los artículos
df = scrape_articles_in_dataframe(df)
- Análisis de SEO semántico con Gemini: vamos a pedirle en una primera función a Gemini que revise los artículos scrapeados y los analise semánticamente.
import time
# Función para hacer la llamada a Gemini 1.5 Flash y obtener un resumen y análisis SEO
def gemini_summarize_and_analyze(text):
try:
prompt = f"""You are an expert in semantic SEO optimization and content strategy.
Summarize the following article and extract the most important points related to SEO, including keywords, bigraphs, trigrams, structure, meta tags and content strategy used. Focus on identifying strengths and weaknesses in SEO practices to determine what makes this article rank high in search engines. Keep in mind that all of this analysis will serve as the basis for creating new content that will outperform them in SEO.
Artículo: {text}
"""
response = model.generate_content(prompt)
return response.text
except Exception as e:
print(f"Error al generar el análisis con Gemini 1.5 Flash: {str(e)}")
return "Error al generar análisis con Gemini 1.5 Flash"
# Función principal para procesar los artículos scrapeados
def process_scraped_articles(df):
if df.empty:
print("El DataFrame está vacío, no hay datos para procesar.")
return df
for index, row in df.iterrows():
article_text = row['scraped_text']
if not article_text.strip(): # Verifica si el texto está vacío
print(f"Artículo vacío para la URL: {row['link']}, omitiendo análisis.")
df.at[index, 'SEO Analysis'] = "No se pudo scrapear el texto del artículo"
continue
# Llamada a Gemini 1.5 Flash para analizar cada artículo
print(f"Procesando artículo {index + 1}/{len(df)}: {row['link']}")
summary_and_seo_analysis = gemini_summarize_and_analyze(article_text)
df.at[index, 'SEO Analysis'] = summary_and_seo_analysis
time.sleep(2) # Agrega un pequeño retraso para no sobrecargar la API
# Guardar el DataFrame a CSV
output_csv = "Gemini_SEO_Analysis_on_SERP_Links_Text.csv"
df.to_csv(output_csv, index=False)
print(f"Archivo CSV generado: {output_csv}")
return df
# Llama a la función para procesar los artículos scrapeados y generar el CSV
df = process_scraped_articles(df)
- Generación de una guía SEO: esta función toma el análisis SEO anterior y lo traduce en una guía para el escritor, asegurándose de que cubra los puntos semánticos necesarios para maximizar la relevancia del contenido en los resultados de búsqueda.
def generar_guia_escritor_con_gemini(df):
if 'SEO Analysis' not in df.columns:
print("La columna 'SEO Analysis' no está presente en el DataFrame.")
return
# Concatenar todos los análisis SEO en un solo texto
contexto_seo = "\n\n".join(df['SEO Analysis'].dropna().tolist())
# Verificar el contenido de contexto_seo
#print("Contenido de contexto_seo:", contexto_seo) # Añadir esta línea para depuración
# Prompt para Gemini
prompt = f"""
You are an expert at Semantic SEO. In particular, you are superhuman at taking the result of an SEO analysis of a search engine results page for a given keyword: {contexto_seo}.
Using it to build a readout/guide that can be used to inform someone writing a long-form article about a given topic so that they can best fully cover the semantic SEO
as shown in the SERP. The goal of this guide is to help the writer make sure that the content they are creating is as comprehensive to the semantic SEO
expressed in the content that ranks on the first page of Google for the given {query}. With the following semantic data, please provide this readout/guide.
This readout/guide should be useful to someone writing about the topic, and should not include instructions to add info to the article about the SERP itself.
The SERP semantic SEO data is just to be used to help inform the guide/readout. Please provide the readout/guide in well organized and hierarchical markdown.
Output Lenguage: spanish. Codification: UTF-8"
"""
try:
# Generar la guía con Gemini
response = model.generate_content(prompt)
guia_escritor = response.text
print("Guía para el escritor generada con éxito.")
return guia_escritor
except Exception as e:
print(f"Error al generar la guía con Gemini: {str(e)}")
return "Error al generar la guía con Gemini"
# Llamar a la función para generar la guía
guia_para_escritor = generar_guia_escritor_con_gemini(df)
# Imprimir la guía generada
print("\nGuía para el escritor:")
print(guia_para_escritor)
• Estructuración de un esquema exhaustivo: A partir de la guía, el script desarrolla un esquema jerárquico detallado, ayudando al escritor a cubrir todas las secciones relevantes y aprovechar las palabras clave en cada subtítulo y párrafo del artículo. Es una especie de indice en formato markdown que puede ser iterado posteriormente. NOTA: me pasó que Gemini cambiaba la manera de marcar el inicio de las secciones por ejemplo con ### y aveces con ***. Te en cuenta el formato de salida que arroja para ajustarlo en la próxima función o en el propio prompt.
def generar_outline_con_gemini(guia_para_escritor, query):
prompt = f"""
Use the following writer's guide: {guia_para_escritor} and generate an incredibly thorough article outline.
Consider all possible angles and be as thorough as possible. Do not add or modify the indications of the guide,
just develop each of the sections for our outline. Please provide the readout/guide in well organized and hierarchical markdown.
Output Lenguage: spanish. Codification: UTF-8
"""
try:
response = model.generate_content(prompt)
outline = response.text
print("Esquema generado con éxito.")
return outline
except Exception as e:
print(f"Error al generar el esquema con Gemini: {str(e)}")
return "Error al generar el esquema con Gemini"
# Llamar a la función para generar el esquema
esquema_articulo = generar_outline_con_gemini(guia_para_escritor, query)
# Guardar el esquema en un archivo markdown
with open("esquema_articulo.md", "w", encoding="utf-8") as f:
f.write(esquema_articulo)
print("\nEsquema del artículo guardado en 'esquema_articulo.md'")
• Desarrollo de contenido para cada sección: Finalmente, la función de generación de contenido elabora el texto completo para cada sección del esquema, cumpliendo con las directrices SEO y agregando detalles que optimizan el contenido de cara a los motores de búsqueda.
def generar_contenido_seccion(seccion, esquema_articulo):
prompt = f"""
Based on the outline provided, develop the content for the next section of the article.
The content should develop only what defines the indicated section, should be informative and optimized for SEO.
Be sure to follow these guidelines:
1. **Introduction for the main section**: If it is a main heading, start with a brief introduction that summarizes the objective or relevance of the section.
2. **Subsection development**: For each subsection, provide a detailed and complete development. Prioritize explanatory paragraphs over lists, except if the list is necessary for clarity.
3. **Expansion of ideas**: Provide specific examples and details to optimize content for SEO, without conclusions or redundancies.
Article outline:
{article_outline}
Section to develop:
{section}
Output format: Markdown
Output language: English
Encoding: UTF-8
"""
try:
response = model.generate_content(prompt)
contenido_seccion = response.text
print(f"Contenido generado con éxito para la sección: {seccion[:50]}...")
return contenido_seccion
except Exception as e:
print(f"Error al generar contenido para la sección {seccion[:50]}...: {str(e)}")
return f"Error al generar contenido para la sección: {seccion[:50]}..."
- Edición final del contenido: finalmente pasamos el contenido generado por un proceso de edición que lo enriquece en extensión y lo «cura» en tono y para reducir señales que lo clasifican como contenido 100% IA.
def mejorar_contenido(contenido_seccion):
prompt = f"""
Act as editor and review the following content by expanding the subsections.
Be sure to:
1. expand each subsection with additional details, examples, and context where appropriate.
2. Convert lists to descriptive paragraphs, except when a list would improve the clarity of the content.
3. Review and optimize content for SEO, without adding conclusions.
4. Vary the structure and flow of the text to:
a. Diversify vocabulary and style.
b. Incorporate idiomatic and colloquial expressions.
c. Introduce minor typographical or grammatical errors.
d. Add emotions, humor, or sarcasm (when appropriate).
e. Include a personal voice
f. Use varied transitions and connectors.
Initial content:
{section_content}
Output format: Markdown
Output language: English
Encoding: UTF-8
"""
try:
response = model.generate_content(prompt)
contenido_mejorado = response.text
print("Contenido mejorado con éxito.")
return contenido_mejorado
except Exception as e:
print(f"Error al mejorar contenido: {str(e)}")
return f"Error al mejorar contenido."
def procesar_esquema(esquema_path, guia_para_escritor):
contenido_mejorado = []
seccion_actual = []
with open(esquema_path, 'r', encoding='utf-8') as f:
for linea in f:
if linea.startswith("* **"):
if seccion_actual:
seccion_texto = ''.join(seccion_actual).strip()
contenido_seccion = generar_contenido_seccion(seccion_texto, guia_para_escritor)
contenido_seccion = mejorar_contenido(contenido_seccion)
contenido_mejorado.append(contenido_seccion)
seccion_actual = []
seccion_actual.append(linea)
elif linea.startswith("**"):
seccion_actual.append(linea)
elif seccion_actual:
seccion_actual.append(linea)
if seccion_actual:
seccion_texto = ''.join(seccion_actual).strip()
contenido_seccion = generar_contenido_seccion(seccion_texto, guia_para_escritor)
contenido_seccion = mejorar_contenido(contenido_seccion)
contenido_mejorado.append(contenido_seccion)
return contenido_mejorado
contenido_mejorado = procesar_esquema("esquema_articulo.md", guia_para_escritor)
contenido_completo = "\n\n".join(contenido_mejorado)
print("\nContenido completo generado:")
print(contenido_completo[:500] + "...")
with open("articulo_completo.md", "w", encoding="utf-8") as f:
f.write(contenido_completo)
print("\nEl artículo completo ha sido guardado en 'articulo_completo.md'")
- Publicación en WordPress(opcional): como vimos en un artículo antes podíamos utilizar la API de WordPress para enviar nuestro post generado directamente a las entradas de nuestro sitio web. Pero por refrescar la memoria les dejo este ejemplo de código que he utilizado para hacerlo dentro de este script:
import base64
import requests
import os
def publicar_en_wordpress(titulo, archivo_contenido):
# Obtener credenciales de WordPress desde variables de entorno (debes buscar las tuyas en el administrador de tu wordpress seccion usuario / contraseñas de aplicación.
login = os.getenv('WORDPRESS_LOGIN')
password = os.getenv('WORDPRESS_PASSWORD')
if not login or not password:
print("Error: No se encontraron las credenciales de WordPress en las variables de entorno.")
return
# Configurar la URL de la API de WordPress y los encabezados de autorización
url = 'https://tusitioweb.com/wp-json/wp/v2/posts'
headers = {
'Authorization': 'Basic ' + base64.b64encode(f"{login}:{password}".encode()).decode()
}
# Leer el contenido del archivo
try:
with open(archivo_contenido, 'r', encoding='utf-8') as file:
contenido = file.read()
except FileNotFoundError:
print(f"Error: No se encontró el archivo {archivo_contenido}")
return
except IOError:
print(f"Error: No se pudo leer el archivo {archivo_contenido}")
return
# Preparar el cuerpo de la solicitud
data = {
'title': titulo,
'content': contenido,
'status': 'draft'
}
# Realizar la solicitud POST
try:
response = requests.post(url, headers=headers, json=data)
response.raise_for_status()
print('Entrada creada correctamente en WordPress como borrador.')
except requests.exceptions.RequestException as e:
print(f'Error al crear la entrada en WordPress: {str(e)}')
# Llamar a la función para publicar en WordPress
publicar_en_wordpress("Titulo del Post", "articulo_completo.md")
Beneficios de este script para estrategias de contenidos
Para cualquier estrategia de contenidos, la eficiencia, la precisión y la optimización son clave. Este script automatiza una parte del proceso de creación de contenido que puede ser tediosa y lleva tiempo, permitiendo al equipo enfocarse en aspectos creativos y estratégicos. Estos son algunos beneficios específicos:
- Ahorro de Tiempo en la Investigación de Contenidos
La creación de contenido SEO suele comenzar con una exhaustiva investigación de palabras clave y temas. Este script permite simplificar el proceso, ya que genera un esquema basado en los mejores resultados de búsqueda, lo cual ahorra horas de investigación inicial y proporciona a los redactores un enfoque claro desde el principio.
- Escalabilidad en la Producción de Contenidos
Si el equipo de marketing maneja múltiples temas y nichos, el script puede adaptar su generación de contenidos para distintos términos y análisis SEO, facilitando la producción a escala. Esto permite una rápida creación de guías y artículos en diferentes temáticas, optimizando la inversión de tiempo en contenidos de calidad y alto valor SEO.
- Optimización de Estrategias para el Usuario Final
Además del SEO, el contenido generado está diseñado pensando en el usuario final. Esto se debe a que el esquema y las subsecciones responden a las inquietudes y necesidades de búsqueda de los usuarios reales. Al combinar estas necesidades con los requisitos SEO, el resultado es contenido útil, informativo y atractivo, mejorando la experiencia del usuario y aumentando el tiempo de permanencia en la página.
Beneficios de este script para el SEO
La automatización de contenido con herramientas de IA puede elevar significativamente el nivel de una estrategia de SEO y marketing de contenidos. Estos son algunos de los beneficios principales de este script para el SEO:
- Optimización Semántica Completa
La IA no solo se limita a incluir palabras clave. Nuestra primera función toma los datos de un análisis SEO y los convierte en una guía bien estructurada, asegurando que el escritor cubra todos los aspectos semánticos importantes. Esto significa que el contenido final no solo estará optimizado para una palabra clave específica, sino que abarcará toda la semántica asociada a esa palabra, aumentando la relevancia del contenido para el motor de búsqueda.
- Coherencia y Profundidad del Contenido
Al estructurar un esquema completo basado en los mejores resultados de búsqueda, el script asegura que el contenido no quede superficial. Los motores de búsqueda valoran la profundidad, y esta herramienta genera secciones específicas que permiten un desarrollo informativo y detallado en cada punto, lo cual es clave para retener a los lectores y para un mejor posicionamiento SEO.
- Adaptación a las Búsquedas de Usuario
El script es capaz de crear una estructura que responde a las preguntas y necesidades reales de los usuarios, ya que basa su estructura en el análisis SEO inicial. Al entender lo que los usuarios realmente buscan, el contenido se convierte en una herramienta informativa completa y, en consecuencia, un recurso valioso que puede recibir enlaces y retención de usuarios, factores positivos para el SEO.
- SEO Multilingüe
Otra ventaja de este script es su flexibilidad con el idioma, en este caso, el español. Esto significa que el contenido está optimizado y redactado en el idioma de destino, cumpliendo con las especificaciones SEO del idioma y maximizando el alcance en mercados de habla hispana.
¿Para quien puede ser útil un script de contenidos como este?
Al implementar este script en una estrategia de marketing digital, el equipo puede abordar diversas necesidades de contenido. A continuación, algunos casos prácticos:
• Blogs de empresas y marcas personales: Para empresas que publican regularmente, este script ayuda a asegurar que cada artículo esté bien optimizado y alineado con las búsquedas actuales de los usuarios.
• Agencias de marketing digital: Las agencias pueden utilizar este script para generar de manera rápida y eficaz contenido para varios clientes, mejorando el valor y alcance de sus servicios de SEO.
• Creadores de contenido en plataformas educativas: Para aquellos que publican contenidos educativos y explicativos, como guías paso a paso, el script ayuda a organizar la información de forma clara y coherente, maximizando el valor para los estudiantes.
En resumen, este script ofrece una ventaja competitiva al automatizar y optimizar la creación de contenido SEO, haciendo que el marketing de contenidos sea más estratégico y alineado con las necesidades reales del público y del motor de búsqueda.
Prueba, cambia los prompts, juega, agrega funciones que trabajen otros aspectos, este script ya es todo tuyo =)