8
n8n 中文网amn8n.com

n8n书籍扫描器

高级

这是一个Content Creation, Multimodal AI领域的自动化工作流,包含 16 个节点。主要使用 Set, Code, Webhook, HttpRequest, OpenAi 等节点。 使用GPT-4o和Google Books从书架照片中提取并验证书名

前置要求
  • HTTP Webhook 端点(n8n 会自动生成)
  • 可能需要目标 API 的认证凭证
  • OpenAI API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "n1UbcJnl7I5rMDIe",
  "meta": {
    "instanceId": "7d302cb44fba0420b6a4deb04edff9d7c47e83ef1f3f66f89fe519337b882186",
    "templateCredsSetupCompleted": true
  },
  "name": "n8n提交书籍扫描器",
  "tags": [],
  "nodes": [
    {
      "id": "8fc53cb4-d7bc-4fcb-8c6d-1ee90b4ad5b4",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -896,
        1376
      ],
      "webhookId": "365ea003-fe66-4211-ae03-69f1456d768e",
      "parameters": {
        "path": "365ea003-fe66-4211-ae03-69f1456d768e",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "a140d496-9098-43b2-97df-089a811a909d",
      "name": "响应 Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        592,
        1376
      ],
      "parameters": {
        "options": {
          "responseCode": 200,
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              }
            ]
          }
        },
        "respondWith": "json",
        "responseBody": "={{$json}}"
      },
      "typeVersion": 1.4
    },
    {
      "id": "050a5432-4d7e-4855-b09f-5ca40a9e0999",
      "name": "分析图片",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -448,
        1376
      ],
      "parameters": {
        "text": "=You are a STRICT transformer. Analyze the image of book spines and return only clearly readable titles and authors. \nDo NOT guess. If the author isn't clearly visible, set \"author\": null.\nNormalize capitalization. Deduplicate by title. \nOutput STRICT JSON only:\n{\"books\":[{\"title\":\"string\",\"author\":\"string|null\"}]}\n",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {},
        "resource": "image",
        "imageUrls": "={{$json.image}}\n",
        "operation": "analyze"
      },
      "credentials": {
        "openAiApi": {
          "id": "iJ4uczBur5RBMvV4",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "50c4d4b8-b164-47fb-b9c6-2234f3cb5952",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -992,
        1280
      ],
      "parameters": {
        "height": 80,
        "content": "Webhook连接到前端,传递包含imageURL(字符串)的JSON"
      },
      "typeVersion": 1
    },
    {
      "id": "d245e1df-a5a2-487c-bd8f-41601be2f05c",
      "name": "便签 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -736,
        1520
      ],
      "parameters": {
        "height": 80,
        "content": "输入被规范化"
      },
      "typeVersion": 1
    },
    {
      "id": "2c44a9af-f395-45ff-bf4f-91a55c829d3e",
      "name": "便签 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -512,
        1280
      ],
      "parameters": {
        "height": 80,
        "content": "图像被分析和转换。"
      },
      "typeVersion": 1
    },
    {
      "id": "1ec82294-d70b-4ff6-ab34-c7ef156a41b2",
      "name": "便签 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        1520
      ],
      "parameters": {
        "content": "将输出(本例中为书籍)拆分为单独的项目,为下一步验证书籍与已知来源以确认标题和作者做准备。"
      },
      "typeVersion": 1
    },
    {
      "id": "44f65fae-f829-4b6b-976b-001d071b82ee",
      "name": "便签 4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        1280
      ],
      "parameters": {
        "height": 80,
        "content": "针对Google Books确认每个标题"
      },
      "typeVersion": 1
    },
    {
      "id": "917a2da3-3691-4904-8cbc-4c00ee4ed72e",
      "name": "便签 5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        112,
        1536
      ],
      "parameters": {
        "height": 80,
        "content": "规范化数据"
      },
      "typeVersion": 1
    },
    {
      "id": "62b507e0-5b7d-48a3-9e44-aa4ccf339331",
      "name": "便签6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        304,
        1280
      ],
      "parameters": {
        "height": 80,
        "content": "重新聚合书籍列表并去重"
      },
      "typeVersion": 1
    },
    {
      "id": "94b96cbc-3bd4-4db8-83ff-eb57b4e516c3",
      "name": "便签7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        544,
        1520
      ],
      "parameters": {
        "height": 80,
        "content": "将列表返回给前端。"
      },
      "typeVersion": 1
    },
    {
      "id": "c4796062-4c64-474a-85b9-8306585c18b1",
      "name": "输入规范化",
      "type": "n8n-nodes-base.set",
      "position": [
        -672,
        1376
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "94b88376-fed9-46cf-882f-d4c0d7670350",
              "name": "image",
              "type": "string",
              "value": "={{ ($json.body?.imageUrl || $json.body?.image || $json.imageUrl || $json.image || '').trim() }}\n"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "dbe25007-44da-403b-89bf-bcbf57591d58",
      "name": "项目列表拆分",
      "type": "n8n-nodes-base.code",
      "position": [
        -240,
        1376
      ],
      "parameters": {
        "jsCode": "const items = await $input.all();\nconst out = [];\n\nfunction stripCodeFence(s) {\n  return String(s || '')\n    .replace(/^```json\\s*/i, '')\n    .replace(/^```\\s*/i, '')\n    .replace(/```$/, '')\n    .trim();\n}\n\nfunction firstAuthor(a) {\n  if (!a) return null;\n  // keep a single name for better \"inauthor:\" matching\n  const s = String(a);\n  const parts = s.split(/\\s*(?:,| and |&)\\s*/i);\n  return (parts[0] || '').trim() || null;\n}\n\nfor (const item of items) {\n  let books = null;\n\n  // Case 1: you already have an object with books[]\n  if (Array.isArray(item.json?.books)) {\n    books = item.json.books;\n  }\n\n  // Case 2: you have a string in `content` with ```json ... ```\n  if (!books && typeof item.json?.content === 'string') {\n    const cleaned = stripCodeFence(item.json.content);\n    try {\n      const parsed = JSON.parse(cleaned);\n      if (Array.isArray(parsed.books)) books = parsed.books;\n    } catch (e) {\n      // ignore; we'll fall back\n    }\n  }\n\n  if (!books) continue;\n\n  for (const b of books) {\n    out.push({\n      json: {\n        title: b.title,\n        author: b.author ?? null,\n        // helper field only for the search query:\n        searchAuthor: firstAuthor(b.author)\n      }\n    });\n  }\n}\n\nreturn out;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "2017d011-6d85-4844-a08a-0a7e88ae052d",
      "name": "标题验证",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -32,
        1376
      ],
      "parameters": {
        "url": "https://www.googleapis.com/books/v1/volumes",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "=q",
              "value": "={{ \n  'intitle:\"' + $json.title.replace(/\"/g,'') + '\"' +\n  ($json.searchAuthor ? ' inauthor:\"' + $json.searchAuthor.replace(/\"/g,'') + '\"' : '')\n}}\n"
            },
            {
              "name": "maxResults",
              "value": "5"
            },
            {
              "name": "printType",
              "value": "books"
            },
            {
              "name": "orderBy",
              "value": "relevance"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "7d94ce36-fc7c-44cb-8df6-6f13293160a4",
      "name": "数据规范化",
      "type": "n8n-nodes-base.set",
      "position": [
        176,
        1376
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "67464bb0-8615-41a5-8408-11a95708d200",
              "name": "=title",
              "type": "string",
              "value": "={{ $json.items?.[0]?.volumeInfo?.title || $prevNode('Code').json.title }}\n"
            },
            {
              "id": "8db9c2f0-3193-428e-adab-2745f397233c",
              "name": "author",
              "type": "string",
              "value": "={{ $json.items?.[0]?.volumeInfo?.authors?.[0] || $prevNode('Code').json.author || null }}\n"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "99d74571-f35d-4086-93ce-f7f67768a8f0",
      "name": "重新聚合列表",
      "type": "n8n-nodes-base.code",
      "position": [
        384,
        1376
      ],
      "parameters": {
        "jsCode": "const items = await $input.all();\nconst seen = new Set();\nconst books = [];\n\nfor (const it of items) {\n  const t = (it.json.title || '').toLowerCase().trim();\n  if (t && !seen.has(t)) {\n    seen.add(t);\n    books.push({ title: it.json.title, author: it.json.author ?? null });\n  }\n}\n\nreturn [{ json: { books } }];\n"
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "5f454fe8-f8b7-4302-820e-ec24a00f13bd",
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Input normalized",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze image": {
      "main": [
        [
          {
            "node": "Item list split",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data normalized": {
      "main": [
        [
          {
            "node": "Reaggregates list",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Item list split": {
      "main": [
        [
          {
            "node": "Title validation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Input normalized": {
      "main": [
        [
          {
            "node": "Analyze image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Title validation": {
      "main": [
        [
          {
            "node": "Data normalized",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reaggregates list": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

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

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

高级 - 内容创作, 多模态 AI

需要付费吗?

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

工作流信息
难度等级
高级
节点数量16
分类2
节点类型7
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

外部链接
在 n8n.io 查看

分享此工作流