8
n8n 中文网amn8n.com

完整的SEO审计工作流,带自动化HTML摘要

中级

这是一个Market Research领域的自动化工作流,包含 7 个节点。主要使用 Set, Code, Html, Gmail, HttpRequest 等节点。 每日SEO审计工作流,通过Gmail/Slack发送HTML报告

前置要求
  • Google 账号和 Gmail API 凭证
  • 可能需要目标 API 的认证凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "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,'&lt;').replace(/>/g,'&gt;')}</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,'&lt;').replace(/>/g,'&gt;');\n              const sug = $node[\"Run SEO Audit Logic\"].json.suggestions[i.code]\n                            .replace(/</g,'&lt;').replace(/>/g,'&gt;');\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,'&lt;').replace(/>/g,'&gt;');\n              const sug = $node[\"Run SEO Audit Logic\"].json.suggestions[i.code]\n                            .replace(/</g,'&lt;').replace(/>/g,'&gt;');\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
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

中级 - 市场调研

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
中级
节点数量7
分类1
节点类型7
难度说明

适合有一定经验的用户,包含 6-15 个节点的中等复杂度工作流

外部链接
在 n8n.io 查看

分享此工作流

分类

分类: 34