Volver al inicio

Configuración de pines automáticos de Pinterest para el sitio

Guía para configurar pines automáticos de Pinterest para sitios estáticos. Verificación de dominio, etiquetas meta de artículo, script de Node.js para generación de RSS desde HTML. Conexión a Pinterest Business para tráfico.

Pines automáticos de Pinterest: Generador de RSS en Node.js
Advertisement 728x90

Automatización de Pines de Pinterest para Sitios Estáticos mediante RSS y Metaetiquetas

Para integrar tu sitio con Pinterest, comienza verificando tu dominio. En el Centro de Negocios de Pinterest, elige el método de metaetiqueta e insértala en el <head> de tu página de inicio:

<meta name="p:domain_verify" content="12ac6ff1eaa70a3386sg439fda58bbb6"/>

Esto permite a Pinterest indexar páginas con Pines Enriquecidos. Luego, añade metaetiquetas al <head> de cada artículo:

<meta name="is_article" content="true">
<meta name="pinterest_description" content="Descripción del pin, 150–200 caracteres">
<meta name="pinterest_image" content="https://example.com/image.webp">
<meta name="article_published_at" content="2026-04-03T12:00:00+03:00">

La imagen debe ser vertical (1000×1500 px), servida mediante HTTPS y menor a 2 MB. Sin pinterest_image, Pinterest usa la primera <img> de la página, lo que a menudo no es óptimo.

Google AdInline article slot

Generación de un Feed RSS con un Script de Node.js

Pinterest analiza RSS para crear pines automáticamente. Usa un script para escanear archivos HTML, extraer metadatos y generar rss.xml.

Crea generate-rss.js en una carpeta scripts:

import fs from 'node:fs/promises';
import path from 'node:path';
import * as cheerio from 'cheerio';

// ================== CONFIGURACIÓN (PERSONALIZA ESTO) ==================

const BASE_URL = 'https://TU_SITIO.com';
const ROOT_DIR = path.resolve(process.cwd(), 'dist');
const OUTPUT_RSS_PATH = path.join(ROOT_DIR, 'rss.xml');
const CHANNEL_TITLE = 'Nombre de tu Blog';
const CHANNEL_DESCRIPTION = 'Descripción: artículos sobre desarrollo, SEO, diseño';

// ================== NO MODIFIQUE A PARTIR DE AQUÍ ==================

function escapeXml(value = '') {
  return value
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&apos;');
}

function getMimeTypeByExt(urlOrPath = '') {
  const ext = urlOrPath.split('.').pop()?.toLowerCase();
  switch (ext) {
    case 'jpg': case 'jpeg': return 'image/jpeg';
    case 'png': return 'image/png';
    case 'webp': return 'image/webp';
    case 'gif': return 'image/gif';
    default: return 'image/*';
  }
}

function buildArticleUrl(filePath) {
  const relativePath = path.relative(ROOT_DIR, filePath).replace(/\\/g, '/');
  return `${BASE_URL}/${relativePath}`;
}

function buildImageUrl(src) {
  if (!src) return '';
  if (/^https?:\/\//i.test(src)) return src;
  if (src.startsWith('/')) return `${BASE_URL}${src}`;
  return `${BASE_URL}/${src}`;
}

async function collectHtmlFiles(dir) {
  const entries = await fs.readdir(dir, { withFileTypes: true });
  const files = [];
  for (const entry of entries) {
    const fullPath = path.join(dir, entry.name);
    if (entry.isDirectory()) {
      const nested = await collectHtmlFiles(fullPath);
      files.push(...nested);
    } else if (entry.isFile() && entry.name.endsWith('.html')) {
      files.push(fullPath);
    }
  }
  return files;
}

async function parseArticle(filePath) {
  const content = await fs.readFile(filePath, 'utf8');
  const $ = cheerio.load(content);

  const isArticle = $('meta[name="is_article"]').length > 0;
  if (!isArticle) return null;

  const title = $('h1').first().text().trim() || 'Sin Título';
  const pinterestDescription = $('meta[name="pinterest_description"]').attr('content')?.trim() || '';
  const pinterestImg = $('meta[name="pinterest_image"]').attr('content');
  let firstImgSrc = $('img').first().attr('src') || '';
  if (firstImgSrc.endsWith('.svg')) firstImgSrc = '';
  const imageUrl = buildImageUrl(pinterestImg || firstImgSrc);

  let pubDate;
  const articleDateMeta = $('meta[name="article_published_at"]').attr('content');
  if (articleDateMeta) {
    const parsed = new Date(articleDateMeta);
    if (!isNaN(parsed.getTime())) pubDate = parsed;
  }
  if (!pubDate) {
    const stat = await fs.stat(filePath);
    pubDate = stat.mtime;
  }

  return { title, pinterestDescription, url: buildArticleUrl(filePath), imageUrl, pubDate };
}

function buildRssXml(items) {
  const itemsXml = items.map(item => {
    const enclosure = item.imageUrl ? `\n        <enclosure url="${escapeXml(item.imageUrl)}" type="${getMimeTypeByExt(item.imageUrl)}" />` : '';
    return `\n    <item>\n        <title>${escapeXml(item.title)}</title>\n        <link>${escapeXml(item.url)}</link>\n        <description><![CDATA[${item.pinterestDescription}]]></description>\n        <pubDate>${item.pubDate.toUTCString()}</pubDate>${enclosure}\n    </item>`;
  }).join('\n');

  return `<?xml version="1.0" encoding="UTF-8"?>\n<rss version="2.0">\n<channel>\n    <title>${escapeXml(CHANNEL_TITLE)}</title>\n    <link>${escapeXml(BASE_URL)}</link>\n    <description>${escapeXml(CHANNEL_DESCRIPTION)}</description>${itemsXml}\n</channel>\n</rss>`;
}

async function main() {
  console.log('🔍 Buscando HTML en:', ROOT_DIR);
  const htmlFiles = await collectHtmlFiles(ROOT_DIR);
  console.log(`📄 Archivos HTML encontrados: ${htmlFiles.length}`);

  const articles = [];
  for (const filePath of htmlFiles) {
    const article = await parseArticle(filePath);
    if (article) articles.push(article);
  }
  console.log(`📰 Artículos con is_article: ${articles.length}`);

  if (!articles.length) {
    console.warn('⚠️ No se encontraron artículos. RSS no creado.');
    return;
  }

  articles.sort((a, b) => b.pubDate - a.pubDate);
  const rssXml = buildRssXml(articles);
  await fs.writeFile(OUTPUT_RSS_PATH, rssXml, 'utf8');
  console.log(`✅ RSS guardado: ${OUTPUT_RSS_PATH}`);
}

main().catch(err => {
  console.error('❌ Error:', err);
  process.exit(1);
});

Para ejecutar:

Google AdInline article slot
  • npm install cheerio
  • Configura BASE_URL, ROOT_DIR y los detalles del canal.
  • node generate-rss.js

El script escanea HTML recursivamente, analiza con Cheerio, escapa XML, determina tipos MIME por extensión y construye URLs absolutas. Ordena por pubDate.

Conexión del RSS a Pinterest y Automatización

En Pinterest: "Crear" → "Pines Enriquecidos" → "Feed RSS" → pega https://tu-sitio.com/rss.xml. Los pines se generan en 6–24 horas.

Proceso:

Google AdInline article slot
  • Añade un artículo con metaetiquetas.
  • Ejecuta node generate-rss.js.
  • Pinterest analiza el RSS y crea pines desde <title>, <description> y <enclosure>.

Para automatización completa, integra en CI/CD (por ejemplo, GitHub Actions) o cron: 0 0 * cd /ruta/al/proyecto && node scripts/generate-rss.js.

Ventajas de este enfoque:

  • Funciona con sitios estáticos (Hugo, Eleventy, HTML plano).
  • No requiere CMS ni plugins.
  • Escala a miles de artículos.
  • Pinterest almacena en caché el RSS, por lo que no se necesitan actualizaciones en tiempo real.

Puntos Clave

  • Verifica tu dominio con la metaetiqueta p:domain_verify para indexación.
  • Usa pinterest_image para controlar las imágenes de los pines.
  • El script maneja alternativas como mtime del archivo y la primera <img>.
  • RSS incluye <enclosure> para imágenes con tipos MIME.
  • Automatiza mediante cron o CI para producción.

— Editorial Team

Advertisement 728x90

Leer después