Flujo de trabajo completo de auditoría SEO con resumen HTML automatizado
Este es unMarket Researchflujo de automatización del dominio deautomatización que contiene 7 nodos.Utiliza principalmente nodos como Set, Code, Html, Gmail, HttpRequest. Flujo de trabajo de auditoría SEO diario, envío de informe HTML por correo electrónico/Gmail
- •Cuenta de Google y credenciales de API de Gmail
- •Pueden requerirse credenciales de autenticación para la API de destino
Nodos utilizados (7)
Categoría
{
"id": "ak2jPqnpYMKMo8Bp",
"meta": {
"instanceId": "e0fd1578cd5453d4707742d6846391bbf1c4d865d540d8236161c7a6376cbd50",
"templateCredsSetupCompleted": true
},
"name": "Complete SEO Audit Workflow with Automated HTML Summaries",
"tags": [],
"nodes": [
{
"id": "2fa7bca3-02c4-4ded-b4ea-b8dca5314257",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
144,
-560
],
"parameters": {
"width": 796,
"height": 644,
"content": "## Complete SEO Audit Workflow with Automated HTML Reports via Gmail or Slack\n\n### Purpose \nRun daily, in-depth audits of specified pages, checking everything from meta tags and heading structure to Core Web Vitals, accessibility, structured data, and security headers—so you catch issues before they impact your rankings.\n\n### Benefits \n- **Proactive Monitoring:** Identify on-page and technical SEO errors as they arise. \n- **Time Savings:** Automate repetitive checks and free your team to focus on strategy. \n- **Actionable Insights:** “Top 3 Priorities” and categorized issues speed up remediation.\n\n### Output \n• A formatted HTML report delivered via Gmail (or Slack) \n• Clear, actionable “Top 3 Priorities,” categorized On-Page vs. Technical issues, and next-steps links\n\n### Requirements & Customization \n- **Gmail/Slack Credentials:** For report delivery. \n- **Easy Customization:** \n - Edit URLs & recipients in the “Set Variables” node. \n - Tweak audit frequency in the Schedule Trigger. \n - Enhance the Code node with extra checks or third-party integrations. \n"
},
"typeVersion": 1
},
{
"id": "b64d81da-cc22-4fdd-9146-b76a29721047",
"name": "Daily Audit Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
128,
144
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "b12cf011-73cc-4890-bf15-1292201d3f60",
"name": "Configure Target & Recipients",
"type": "n8n-nodes-base.set",
"position": [
320,
144
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "44337ce3-4e65-4d7b-9b1a-cf77f8412169",
"name": "siteUrl",
"type": "string",
"value": "https://example.com/"
},
{
"id": "60e53201-5d1b-4665-9f13-aad194cd653c",
"name": "emailFrom",
"type": "string",
"value": "exampleFrom@example.com"
},
{
"id": "dc19de25-3f64-4244-9a3e-65d5178181c5",
"name": "emailTo",
"type": "string",
"value": "exampleTo@example.com"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "d84cbda7-a9e0-4c40-86da-e8914cf99e8f",
"name": "HTTP Fetch Page Content",
"type": "n8n-nodes-base.httpRequest",
"position": [
528,
144
],
"parameters": {
"url": "={{$node[\"Configure Target & Recipients\"].json.siteUrl}}\n",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "f25b7ba0-54e9-4e5e-85f8-936f9ffd3930",
"name": "Parse On‑Page Elements",
"type": "n8n-nodes-base.html",
"position": [
736,
144
],
"parameters": {
"options": {},
"operation": "extractHtmlContent",
"dataPropertyName": "=data",
"extractionValues": {
"values": [
{
"key": "title",
"cssSelector": "title"
},
{
"key": "metaDesc",
"attribute": "content",
"cssSelector": "meta[name=\"description\"]",
"returnValue": "attribute"
},
{
"key": "h1Tags",
"cssSelector": "h1",
"returnArray": true
},
{
"key": "h2Tags",
"cssSelector": "h2",
"returnArray": true
},
{
"key": "imgAlts",
"attribute": "alt",
"cssSelector": "img",
"returnArray": true,
"returnValue": "attribute"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "f097edcb-fb09-4eb7-96dc-398f8e3dbecd",
"name": "Run SEO Audit Logic",
"type": "n8n-nodes-base.code",
"position": [
944,
144
],
"parameters": {
"jsCode": "// Collect results for each URL\nconst output = [];\n\nfor (const item of items) {\n const d = item.json;\n const issues = [];\n const suggestions = {};\n\n function addIssue(code, message, suggestion) {\n issues.push({ code, message });\n suggestions[code] = suggestion;\n }\n\n // 1. Title length\n if (!d.title) {\n addIssue('missing_title', 'Missing <title>', 'Add a descriptive <title> (30–60 chars).');\n } else if (d.title.length < 30) {\n addIssue('short_title', `Title too short (${d.title.length} chars)`, 'Increase title length to ≥30 chars.');\n } else if (d.title.length > 60) {\n addIssue('long_title', `Title too long (${d.title.length} chars)`, 'Reduce title to ≤60 chars.');\n }\n\n // 2. Meta description\n if (!d.metaDesc) {\n addIssue('missing_meta_desc', 'Missing meta description', 'Add <meta name=\"description\"> (50–160 chars).');\n } else if (d.metaDesc.length < 50) {\n addIssue('short_meta_desc', `Meta description too short (${d.metaDesc.length} chars)`, 'Increase description to ≥50 chars.');\n } else if (d.metaDesc.length > 160) {\n addIssue('long_meta_desc', `Meta description too long (${d.metaDesc.length} chars)`, 'Shorten description to ≤160 chars.');\n }\n\n // 3. Robots, viewport, canonical, lang\n if (!d.metaRobots) {\n addIssue('missing_robots', 'Missing meta robots tag', 'Add <meta name=\"robots\" content=\"index,follow\">.');\n }\n if (!d.metaViewport || !d.metaViewport.includes('width=device-width')) {\n addIssue('viewport', 'Missing/incorrect viewport tag', 'Add <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">.');\n }\n if (!d.linkCanonical) {\n addIssue('canonical', 'Missing canonical', 'Add <link rel=\"canonical\" href=\"YOUR_URL\">.');\n }\n if (!d.htmlLang) {\n addIssue('lang', 'Missing lang attribute', 'Add <html lang=\"en\">.');\n }\n\n // 4. Word count\n if (typeof d.wordCount === 'number' && d.wordCount < 300) {\n addIssue('low_word_count', `Low word count (${d.wordCount} words)`, 'Aim for ≥300 words.');\n }\n\n // 5. Headings\n const h1 = (d.h1Tags || []).length;\n const h2 = (d.h2Tags || []).length;\n const h3 = (d.h3Tags || []).length;\n if (h1 !== 1) {\n addIssue('h1', `${h1} <h1> tags`, 'Use exactly one <h1>.');\n }\n if (h2 === 0) {\n addIssue('h2', 'No <h2> tags', 'Add ≥1 <h2> for sections.');\n }\n if (h3 === 0) {\n addIssue('h3', 'No <h3> tags', 'Use <h3> for subsections.');\n }\n\n // 6. Images alt text\n if (Array.isArray(d.imgAlts)) {\n const missing = d.imgAlts.filter(a => !a || !a.trim()).length;\n if (missing) {\n addIssue('img_alt', `${missing} images missing alt`, 'Provide descriptive alt text.');\n }\n }\n\n // 7. Broken links\n if (Array.isArray(d.links)) {\n const broken = d.links.filter(l => l.status >= 400).length;\n if (broken) {\n addIssue('broken_links', `${broken} broken links`, 'Fix or remove broken URLs.');\n }\n }\n\n // 8. Structured data\n if (!d.structuredData || d.structuredData.length === 0) {\n addIssue('schema', 'No structured data', 'Implement JSON-LD schema markup.');\n }\n\n // 9. Core Web Vitals\n if (d.metrics) {\n const { LCP, INP, CLS } = d.metrics;\n if (LCP > 2500) {\n addIssue('lcp', `LCP too slow (${LCP} ms)`, 'Optimize images and server response.');\n }\n if (INP > 200) {\n addIssue('inp', `INP high (${INP} ms)`, 'Minimize JavaScript execution.');\n }\n if (CLS > 0.1) {\n addIssue('cls', `CLS unstable (${CLS})`, 'Set image dimensions and pre-load fonts.');\n }\n }\n\n // Summary\n const pass = issues.length === 0;\n const summary = pass ? 'All checks passed! 🎉' : `${issues.length} issue${issues.length > 1 ? 's' : ''} detected`;\n\n // Markdown report\n let markdown = `## SEO Audit for ${d.siteUrl || d.url}\\n`;\n markdown += `**Status:** ${pass ? 'PASS ✅' : 'FAIL ❌'}\\n`;\n markdown += `**Summary:** ${summary}\\n`;\n if (!pass) {\n markdown += `\\n### Issues & Suggestions\\n`;\n issues.forEach(({ code, message }) => {\n markdown += `- **${message}** \\n _Suggestion:_ ${suggestions[code]}\\n`;\n });\n }\n\n output.push({\n json: {\n siteUrl: d.siteUrl || d.url,\n pass,\n summary,\n issues,\n suggestions,\n markdownReport: markdown,\n },\n });\n}\n\nreturn output;\n"
},
"typeVersion": 2
},
{
"id": "81cd1593-0669-4f68-bc12-cac12e30f327",
"name": "Email Audit Report",
"type": "n8n-nodes-base.gmail",
"position": [
1168,
144
],
"webhookId": "5394c49f-09fb-4f0f-ba2f-0f312d197b53",
"parameters": {
"sendTo": "={{$node[\"Configure Target & Recipients\"].json.emailTo}}\n\n",
"message": "=<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\" />\n <style>\n body { font-family: Arial, sans-serif; color: #333; line-height: 1.4; padding: 20px; }\n .header { display: flex; align-items: center; margin-bottom: 20px; }\n .logo { height: 40px; margin-right: 15px; }\n .title { font-size: 24px; color: #2a6ebb; margin: 0; }\n .meta { margin: 5px 0 20px; }\n .status-pass { color: green; font-weight: bold; }\n .status-fail { color: red; font-weight: bold; }\n .summary-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }\n .summary-table td { padding: 8px; border: 1px solid #eee; }\n .issues-container { display: flex; gap: 20px; }\n .column { flex: 1; }\n .column h4 { border-bottom: 1px solid #ddd; padding-bottom: 5px; }\n .issues-list { list-style: none; padding: 0; margin: 0; }\n .issues-list li { margin: 8px 0; }\n .suggestion { display: block; margin-left: 15px; font-style: italic; color: #555; }\n .priorities { background: #f9f9f9; padding: 15px; border-left: 4px solid #2a6ebb; margin-bottom: 20px; }\n .next-steps { margin-top: 30px; font-size: 14px; }\n .next-steps a { color: #2a6ebb; text-decoration: none; }\n code { background: #eef; padding: 2px 4px; border-radius: 3px; }\n </style>\n</head>\n<body>\n\n <div class=\"header\">\n <div>\n <h1 class=\"title\">SEO Audit Report</h1>\n <div class=\"meta\">Date: {{ new Date().toLocaleDateString() }}</div>\n </div>\n </div>\n\n <p>\n <strong>Page:</strong> {{ $node[\"Run SEO Audit Logic\"].json.siteUrl }}<br/>\n <strong>Status:</strong>\n <span class=\"{{ $node[\"Run SEO Audit Logic\"].json.pass ? 'status-pass' : 'status-fail' }}\">\n {{ $node[\"Run SEO Audit Logic\"].json.pass ? 'PASS ✅' : 'FAIL ❌' }}\n </span>\n </p>\n\n <div class=\"priorities\">\n <strong>🚩 Top 3 Priorities:</strong>\n <ol>\n {{\n $node[\"Run SEO Audit Logic\"].json.issues\n .slice(0, 3)\n .map(i => `<li>${i.message.replace(/</g,'<').replace(/>/g,'>')}</li>`)\n .join('')\n }}\n </ol>\n </div>\n\n <table class=\"summary-table\">\n <tr>\n <td><strong>Total Issues</strong></td>\n <td>{{ $node[\"Run SEO Audit Logic\"].json.issues.length }}</td>\n </tr>\n <tr>\n <td><strong>Broken Links</strong></td>\n <td>\n {{\n $node[\"Run SEO Audit Logic\"].json.issues.filter(i => i.code === 'broken_links').length\n }}\n </td>\n </tr>\n <tr>\n <td><strong>CWV Alerts</strong></td>\n <td>\n {{\n $node[\"Run SEO Audit Logic\"].json.issues\n .filter(i => ['lcp','inp','cls'].includes(i.code))\n .length\n }}\n </td>\n </tr>\n </table>\n\n <div class=\"issues-container\">\n <!-- On‑Page Issues -->\n <div class=\"column\">\n <h4>📝 On‑Page Issues</h4>\n <ul class=\"issues-list\">\n {{\n $node[\"Run SEO Audit Logic\"].json.issues\n .filter(i => [\n 'missing_title','short_title','long_title',\n 'missing_meta_desc','short_meta_desc','long_meta_desc',\n 'h1','h2','h3','img_alt','schema'\n ].includes(i.code))\n .map(i => {\n const msg = i.message.replace(/</g,'<').replace(/>/g,'>');\n const sug = $node[\"Run SEO Audit Logic\"].json.suggestions[i.code]\n .replace(/</g,'<').replace(/>/g,'>');\n return `\n <li>\n ⚠️ <strong>${msg}</strong>\n <span class=\"suggestion\">\n Suggestion: <code>${sug}</code>\n </span>\n </li>`;\n })\n .join('')\n }}\n </ul>\n </div>\n\n <!-- Technical Issues -->\n <div class=\"column\">\n <h4>🔧 Technical Issues</h4>\n <ul class=\"issues-list\">\n {{\n $node[\"Run SEO Audit Logic\"].json.issues\n .filter(i => ![\n 'missing_title','short_title','long_title',\n 'missing_meta_desc','short_meta_desc','long_meta_desc',\n 'h1','h2','h3','img_alt','schema'\n ].includes(i.code))\n .map(i => {\n const msg = i.message.replace(/</g,'<').replace(/>/g,'>');\n const sug = $node[\"Run SEO Audit Logic\"].json.suggestions[i.code]\n .replace(/</g,'<').replace(/>/g,'>');\n return `\n <li>\n ⚙️ <strong>${msg}</strong>\n <span class=\"suggestion\">\n Suggestion: <code>${sug}</code>\n </span>\n </li>`;\n })\n .join('')\n }}\n </ul>\n </div>\n </div>\n\n <div class=\"next-steps\">\n <p><strong>Next Steps:</strong></p>\n <ol>\n <li>Review the above suggestions and assign tickets in your tracker.</li>\n <li>Re‑run this audit after fixes are deployed to confirm all PASS.</li>\n </ol>\n </div>\n\n</body>\n</html>\n",
"options": {},
"subject": "={{$node[\"Configure Target & Recipients\"].json.siteUrl}}"
},
"credentials": {
"gmailOAuth2": {
"id": "jkKHvU2Pb9X5WJk5",
"name": "Gmail account"
}
},
"typeVersion": 2.1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "bc8a99b9-d397-4881-a02e-6aab78b364dc",
"connections": {
"b64d81da-cc22-4fdd-9146-b76a29721047": {
"main": [
[
{
"node": "b12cf011-73cc-4890-bf15-1292201d3f60",
"type": "main",
"index": 0
}
]
]
},
"f097edcb-fb09-4eb7-96dc-398f8e3dbecd": {
"main": [
[
{
"node": "81cd1593-0669-4f68-bc12-cac12e30f327",
"type": "main",
"index": 0
}
]
]
},
"d84cbda7-a9e0-4c40-86da-e8914cf99e8f": {
"main": [
[
{
"node": "f25b7ba0-54e9-4e5e-85f8-936f9ffd3930",
"type": "main",
"index": 0
}
]
]
},
"f25b7ba0-54e9-4e5e-85f8-936f9ffd3930": {
"main": [
[
{
"node": "f097edcb-fb09-4eb7-96dc-398f8e3dbecd",
"type": "main",
"index": 0
}
]
]
},
"b12cf011-73cc-4890-bf15-1292201d3f60": {
"main": [
[
{
"node": "d84cbda7-a9e0-4c40-86da-e8914cf99e8f",
"type": "main",
"index": 0
}
]
]
}
}
}¿Cómo usar este flujo de trabajo?
Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.
¿En qué escenarios es adecuado este flujo de trabajo?
Intermedio - Investigación de mercado
¿Es de pago?
Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.
Flujos de trabajo relacionados recomendados
Mychel Garzon
@mychel-garzonCompartir este flujo de trabajo