完全なSEO監査ワークフロー、HTML要約を自動化

中級

これはMarket Research分野の自動化ワークフローで、7個のノードを含みます。主にSet, Code, Html, Gmail, HttpRequestなどのノードを使用。 毎日のSEO監査ワークフローでHTMLレポートをGmail/Slackに送信

前提条件
  • 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": "付箋",
      "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": "日次監査トリガー",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        128,
        144
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "b12cf011-73cc-4890-bf15-1292201d3f60",
      "name": "ターゲットと受信者の設定",
      "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": "オンページ要素の解析",
      "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": "SEO監査ロジックの実行",
      "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": "監査レポートのメール送信",
      "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