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 submission book scanner",
  "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": "Respond to 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 connects to front end that passes on JSON with imageURL (string)"
      },
      "typeVersion": 1
    },
    {
      "id": "d245e1df-a5a2-487c-bd8f-41601be2f05c",
      "name": "付箋1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -736,
        1520
      ],
      "parameters": {
        "height": 80,
        "content": "Input is normalized"
      },
      "typeVersion": 1
    },
    {
      "id": "2c44a9af-f395-45ff-bf4f-91a55c829d3e",
      "name": "付箋2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -512,
        1280
      ],
      "parameters": {
        "height": 80,
        "content": "Image is analyzed and transformed."
      },
      "typeVersion": 1
    },
    {
      "id": "1ec82294-d70b-4ff6-ab34-c7ef156a41b2",
      "name": "付箋3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        1520
      ],
      "parameters": {
        "content": "Splits the output (in this case, books) into individual items in preparation for the next step which is to verify the book against a known source to confirm the title and author."
      },
      "typeVersion": 1
    },
    {
      "id": "44f65fae-f829-4b6b-976b-001d071b82ee",
      "name": "付箋4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        1280
      ],
      "parameters": {
        "height": 80,
        "content": "Confirms each title against Google Books"
      },
      "typeVersion": 1
    },
    {
      "id": "917a2da3-3691-4904-8cbc-4c00ee4ed72e",
      "name": "付箋5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        112,
        1536
      ],
      "parameters": {
        "height": 80,
        "content": "Normalizes data"
      },
      "typeVersion": 1
    },
    {
      "id": "62b507e0-5b7d-48a3-9e44-aa4ccf339331",
      "name": "付箋6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        304,
        1280
      ],
      "parameters": {
        "height": 80,
        "content": "Reaggregates book list and dedupes"
      },
      "typeVersion": 1
    },
    {
      "id": "94b96cbc-3bd4-4db8-83ff-eb57b4e516c3",
      "name": "付箋7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        544,
        1520
      ],
      "parameters": {
        "height": 80,
        "content": "Returns list back to frontend."
      },
      "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": {
    "8fc53cb4-d7bc-4fcb-8c6d-1ee90b4ad5b4": {
      "main": [
        [
          {
            "node": "c4796062-4c64-474a-85b9-8306585c18b1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "050a5432-4d7e-4855-b09f-5ca40a9e0999": {
      "main": [
        [
          {
            "node": "dbe25007-44da-403b-89bf-bcbf57591d58",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "7d94ce36-fc7c-44cb-8df6-6f13293160a4": {
      "main": [
        [
          {
            "node": "99d74571-f35d-4086-93ce-f7f67768a8f0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "dbe25007-44da-403b-89bf-bcbf57591d58": {
      "main": [
        [
          {
            "node": "2017d011-6d85-4844-a08a-0a7e88ae052d",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "c4796062-4c64-474a-85b9-8306585c18b1": {
      "main": [
        [
          {
            "node": "050a5432-4d7e-4855-b09f-5ca40a9e0999",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2017d011-6d85-4844-a08a-0a7e88ae052d": {
      "main": [
        [
          {
            "node": "7d94ce36-fc7c-44cb-8df6-6f13293160a4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "99d74571-f35d-4086-93ce-f7f67768a8f0": {
      "main": [
        [
          {
            "node": "a140d496-9098-43b2-97df-089a811a909d",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
よくある質問

このワークフローの使い方は?

上記のJSON設定コードをコピーし、n8nインスタンスで新しいワークフローを作成して「JSONからインポート」を選択、設定を貼り付けて認証情報を必要に応じて変更してください。

このワークフローはどんな場面に適していますか?

上級 - コンテンツ作成, マルチモーダルAI

有料ですか?

このワークフローは完全無料です。ただし、ワークフローで使用するサードパーティサービス(OpenAI APIなど)は別途料金が発生する場合があります。

ワークフロー情報
難易度
上級
ノード数16
カテゴリー2
ノードタイプ7
難易度説明

上級者向け、16ノード以上の複雑なワークフロー

外部リンク
n8n.ioで表示

このワークフローを共有

カテゴリー

カテゴリー: 34