完整的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,'<').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
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
中级 - 市场调研
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
竞争对手内容差距分析器:自动化网站主题映射
使用Gemini AI、Apify和Google Sheets分析竞争对手内容差距
If
Set
Code
+
If
Set
Code
30 节点Mychel Garzon
杂项
社区问题监控器与OpenRouter AI、Reddit和论坛爬取
使用OpenRouter AI、Reddit和论坛爬取监控社区问题
Set
Code
Html
+
Set
Code
Html
29 节点Julian Kaiser
市场调研
GA4异常检测与自动Slack和邮件提醒
GA4异常检测与自动Slack和邮件提醒
If
Set
Code
+
If
Set
Code
9 节点Artur
市场调研
使用Gemini分析和邮件报告将简历匹配到职位描述
使用Gemini分析和邮件报告将简历匹配到职位描述
Set
Code
Gmail
+
Set
Code
Gmail
18 节点Mychel Garzon
AI 摘要总结
发送定时n8n发布说明通知到Gmail
基于AI的n8n发布说明摘要通知(通过Gmail与GPT-5-Mini)
Set
Code
Html
+
Set
Code
Html
16 节点Jeff Huera
个人效率
使用 LinkedIn、Google Sheets 和 AI
使用 LinkedIn、Google Sheets 和 AI 简历评分与求职信自动化职位搜索
Set
Code
Html
+
Set
Code
Html
27 节点Jugal