使用 BOAMP API 和 Google Sheets 监控并筛选法国采购招标
高级
这是一个Content Creation, Multimodal AI领域的自动化工作流,包含 55 个节点。主要使用 If, Code, Wait, Filter, HttpRequest 等节点。 使用 BOAMP API 和 Google Sheets 监控并筛选法国采购招标
前置要求
- •可能需要目标 API 的认证凭证
- •Google Sheets API 凭证
使用的节点 (55)
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"meta": {
"instanceId": "393ca9e36a1f81b0f643c72792946a5fe5e49eb4864181ba4032e5a408278263",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "97a8d56f-29ec-4bd7-a9e9-52707fdaa646",
"name": "HTTP 请求",
"type": "n8n-nodes-base.httpRequest",
"position": [
304,
-304
],
"parameters": {
"url": "https://boamp-datadila.opendatasoft.com/api/explore/v2.1/catalog/datasets/boamp/records",
"options": {},
"sendQuery": true,
"sendHeaders": true,
"queryParameters": {
"parameters": [
{
"name": "select",
"value": "idweb,dateparution,objet,nomacheteur,datelimitereponse,type_marche,url_avis,code_departement"
},
{
"name": "order_by",
"value": "dateparution desc"
},
{
"name": "where",
"value": "=dateparution >= \"{{ $now.minus({days: $('Get config').item.json['Période']}).format('yyyy-MM-dd') }}\""
},
{
"name": "limit",
"value": "100"
},
{
"name": "offset",
"value": "={{ $json.Offset }}"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "Accept",
"value": "application/json"
},
{
"name": "User-Agent",
"value": "n8n-workflow-veille"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "fe0416bf-3443-4d8d-a209-303a03dca6b3",
"name": "处理响应",
"type": "n8n-nodes-base.code",
"position": [
1728,
-304
],
"parameters": {
"jsCode": "// Récupérer la réponse de l'API\nconst response = $input.first().json;\nconst results = response.results || [];\nconst totalCount = response.total_count || 0;\n\n// Récupérer l'offset actuel depuis le nœud \"Get Offset\"\nconst currentOffset = $('Get Offset').first().json.Offset || 0;\nconst limit = 100;\n\n// Calculer le prochain offset\nconst nextOffset = currentOffset + limit;\n\n// Vérifier s'il y a encore des données à récupérer\nconst hasMoreData = nextOffset < totalCount && results.length > 0;\n\nreturn {\n records: results,\n currentOffset: currentOffset,\n nextOffset: nextOffset,\n totalCount: totalCount,\n hasMoreData: hasMoreData,\n recordsInBatch: results.length\n};"
},
"typeVersion": 2
},
{
"id": "80744cd7-4e88-4a6e-8935-6798605fda88",
"name": "检查继续循环",
"type": "n8n-nodes-base.if",
"position": [
2352,
-304
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "115caab4-3fbe-492f-aefc-b7b639832df8",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $('Process Response').item.json.hasMoreData }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "ee8d1ff9-7df2-49f0-a8ce-120c6ce252ac",
"name": "在表格中追加行",
"type": "n8n-nodes-base.googleSheets",
"position": [
1376,
-320
],
"parameters": {
"columns": {
"value": {},
"schema": [
{
"id": "Parution",
"type": "string",
"display": true,
"required": false,
"displayName": "Parution",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Objet",
"type": "string",
"display": true,
"required": false,
"displayName": "Objet",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "URL",
"type": "string",
"display": true,
"required": false,
"displayName": "URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Acheteur",
"type": "string",
"display": true,
"required": false,
"displayName": "Acheteur",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Date limite",
"type": "string",
"display": true,
"required": false,
"displayName": "Date limite",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "_metadata",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "_metadata",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "autoMapInputData",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit#gid=0",
"cachedResultName": "All"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit?gid=0#gid=0"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "wBRLUCktxqXE6DVJ",
"name": "Google Sheets account"
}
},
"typeVersion": 4.7
},
{
"id": "fadbf1a6-d255-47fc-9a9c-26ceaa88c69f",
"name": "格式化结果",
"type": "n8n-nodes-base.code",
"position": [
1168,
-320
],
"parameters": {
"jsCode": "// Récupérer la réponse HTTP brute\nconst response = $input.first().json;\nconst results = response.results || [];\n\n// Fonction pour formater la date en dd/MM/yyyy\nfunction formatDate(dateString) {\n if (!dateString) return null;\n \n try {\n const date = new Date(dateString);\n const day = date.getDate().toString().padStart(2, '0');\n const month = (date.getMonth() + 1).toString().padStart(2, '0');\n const year = date.getFullYear();\n return `${day}/${month}/${year}`;\n } catch (error) {\n return dateString;\n }\n}\n\n// Fonction pour générer l'URL du PDF\nfunction generatePdfUrl(urlAvis, dateParution) {\n if (!urlAvis || !dateParution) return null;\n \n try {\n // Extraire l'idweb de l'URL d'annonce\n const match = urlAvis.match(/idweb:(%22)?([^%\"&]+)(%22)?/);\n if (!match) return null;\n \n const idweb = match[2]; // 25-102635\n \n // Extraire le mois de la date de parution\n const date = new Date(dateParution);\n const month = (date.getMonth() + 1).toString().padStart(2, '0');\n const year = date.getFullYear();\n \n // Construire l'URL PDF\n return `https://www.boamp.fr/telechargements/FILES/PDF/${year}/${month}/${idweb}.pdf`;\n \n } catch (error) {\n return null;\n }\n}\n\n// Créer un item séparé pour chaque appel d'offre\nreturn results.map(item => ({\n json: {\n \"Parution\": item.dateparution,\n \"Objet\": item.objet,\n \"URL\": generatePdfUrl(item.url_avis, item.dateparution),\n \"Acheteur\": item.nomacheteur,\n \"Date limite\": formatDate(item.datelimitereponse)\n }\n}));"
},
"typeVersion": 2
},
{
"id": "69b51766-63c7-4e2d-8906-5f29ae0e92e4",
"name": "获取偏移量",
"type": "n8n-nodes-base.googleSheets",
"position": [
-144,
-304
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 862285542,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit#gid=862285542",
"cachedResultName": "Offset"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit?gid=862285542#gid=862285542"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "wBRLUCktxqXE6DVJ",
"name": "Google Sheets account"
}
},
"typeVersion": 4.7
},
{
"id": "adf9bd65-b1af-453f-aa91-7b3a9c4f7fed",
"name": "计划触发器",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-960,
928
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 10 1 * *"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "1d7e0c25-b6a7-468d-99d4-3efbad6dac2e",
"name": "获取全部",
"type": "n8n-nodes-base.googleSheets",
"position": [
-160,
928
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit#gid=0",
"cachedResultName": "All"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit?gid=862285542#gid=862285542"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "wBRLUCktxqXE6DVJ",
"name": "Google Sheets account"
}
},
"typeVersion": 4.7
},
{
"id": "fac28307-193c-4b9f-9e51-a3fd759084e3",
"name": "查询匹配?",
"type": "n8n-nodes-base.if",
"position": [
2144,
960
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "081889ef-d60f-4ea5-8771-484603075cc2",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $('Get query').item.json.hasMatch }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "f68fe0b2-7ccd-4a29-a74d-a8c62936d02b",
"name": "循环遍历招标",
"type": "n8n-nodes-base.splitInBatches",
"position": [
384,
928
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "0074c6c1-6ec4-4036-9213-4e1caf6023cf",
"name": "获取查询",
"type": "n8n-nodes-base.code",
"position": [
1216,
928
],
"parameters": {
"jsCode": "// Récupération des mots-clés depuis l'objet\nconst keywordData = $('Get keyword').first().json['Mot clé'];\nconst keywordsString = keywordData[0]['Mots clés'] || '';\n\n// Transformation de la chaîne en tableau et nettoyage\nconst termesToSearch = keywordsString\n .split(',')\n .map(term => term.trim()) // Supprime les espaces avant/après\n .filter(term => term.length > 0); // Supprime les éléments vides\n\n// Récupération du texte d'entrée depuis le champ \"text\" de la node précédente\nconst inputText = $input.first().json.text || '';\n\n// Fonction pour normaliser le texte (supprime accents, met en minuscules)\nfunction normalizeText(text) {\n return text\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, ''); // Supprime les accents\n}\n\n// Normalisation du texte d'entrée\nconst normalizedInput = normalizeText(inputText);\n\n// Recherche des correspondances\nconst foundTerms = [];\nlet hasMatch = false;\n\ntermesToSearch.forEach(term => {\n const normalizedTerm = normalizeText(term);\n \n // Recherche du terme exact ou avec variations\n if (normalizedInput.includes(normalizedTerm)) {\n foundTerms.push(term);\n hasMatch = true;\n }\n});\n\n// Construction du résultat\nconst result = {\n hasMatch: hasMatch,\n foundTerms: foundTerms,\n totalMatches: foundTerms.length,\n searchedTerms: termesToSearch,\n originalText: inputText.substring(0, 200) + (inputText.length > 200 ? '...' : '') // Extrait pour debug\n};\n\n// Ajout de détails sur les correspondances trouvées\nif (hasMatch) {\n result.matchDetails = foundTerms.map(term => {\n const normalizedTerm = normalizeText(term);\n const index = normalizedInput.indexOf(normalizedTerm);\n return {\n term: term,\n position: index,\n context: inputText.substring(Math.max(0, index - 50), index + term.length + 50)\n };\n });\n}\n\nreturn [{ json: result }];"
},
"typeVersion": 2
},
{
"id": "b6b5f98f-4894-4979-b0e9-8d968559fd93",
"name": "目标招标",
"type": "n8n-nodes-base.googleSheets",
"position": [
2496,
928
],
"parameters": {
"columns": {
"value": {
"URL": "={{ $('Loop Over offres').item.json.URL }}",
"Match": "={{ $('Get query').item.json.foundTerms }}",
"Objet": "={{ $('Loop Over offres').item.json.Objet }}",
"Acheteur": "={{ $('Loop Over offres').item.json.Acheteur }}",
"Parution": "={{ $('Loop Over offres').item.json.Parution }}",
"Date ajout": "={{ DateTime.now().toFormat('dd/MM/yyyy') }}",
"Date limite": "={{ $('Loop Over offres').item.json['Date limite'] }}"
},
"schema": [
{
"id": "Date ajout",
"type": "string",
"display": true,
"required": false,
"displayName": "Date ajout",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Parution",
"type": "string",
"display": true,
"required": false,
"displayName": "Parution",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Objet",
"type": "string",
"display": true,
"required": false,
"displayName": "Objet",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "URL",
"type": "string",
"display": true,
"required": false,
"displayName": "URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Acheteur",
"type": "string",
"display": true,
"required": false,
"displayName": "Acheteur",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Date limite",
"type": "string",
"display": true,
"required": false,
"displayName": "Date limite",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Match",
"type": "string",
"display": true,
"required": false,
"displayName": "Match",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 151804153,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit#gid=151804153",
"cachedResultName": "Target"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit?gid=151804153#gid=151804153"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "wBRLUCktxqXE6DVJ",
"name": "Google Sheets account"
}
},
"typeVersion": 4.7
},
{
"id": "10e05eea-708b-4460-9482-fab208fc3632",
"name": "等待",
"type": "n8n-nodes-base.wait",
"position": [
2800,
272
],
"webhookId": "63dee3e9-695f-42dc-8689-6ef472d8f0d3",
"parameters": {
"amount": 10
},
"typeVersion": 1.1
},
{
"id": "6bd78099-8371-43a0-82e7-84a3890afe3a",
"name": "重置偏移量",
"type": "n8n-nodes-base.googleSheets",
"position": [
2880,
-320
],
"parameters": {
"columns": {
"value": {
"Offset": "0",
"row_number": 2
},
"schema": [
{
"id": "Offset",
"type": "string",
"display": true,
"required": false,
"displayName": "Offset",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"row_number"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 862285542,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit#gid=862285542",
"cachedResultName": "Offset"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit?gid=862285542#gid=862285542"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "wBRLUCktxqXE6DVJ",
"name": "Google Sheets account"
}
},
"typeVersion": 4.7
},
{
"id": "cbb7af10-393b-459b-99dc-6b806c5f4f92",
"name": "HTTP 请求4",
"type": "n8n-nodes-base.httpRequest",
"position": [
624,
928
],
"parameters": {
"url": "={{ $json.URL }}",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "332d9b79-208b-42a5-84f0-cb64f2cef73e",
"name": "从文件提取",
"type": "n8n-nodes-base.extractFromFile",
"position": [
832,
928
],
"parameters": {
"options": {},
"operation": "pdf"
},
"typeVersion": 1
},
{
"id": "122a1cb2-4a35-4120-b693-aae378b8f841",
"name": "等待1",
"type": "n8n-nodes-base.wait",
"position": [
2896,
1024
],
"webhookId": "8dfc6b2f-d2e9-49db-952b-98e1ee306b4c",
"parameters": {},
"typeVersion": 1.1
},
{
"id": "6e69ac57-2209-4e5f-9a45-957c3b2df683",
"name": "过滤器",
"type": "n8n-nodes-base.filter",
"position": [
96,
928
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "16a31f78-cd21-430f-ac05-9b4a4379d67d",
"operator": {
"type": "string",
"operation": "notEquals"
},
"leftValue": "={{ $json.Ok }}",
"rightValue": "Ok"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "088b07af-f113-49b0-be85-f2d3f508e963",
"name": "确定",
"type": "n8n-nodes-base.googleSheets",
"position": [
1776,
928
],
"parameters": {
"columns": {
"value": {
"Ok": "Ok",
"row_number": "={{ $('Loop Over offres').item.json.row_number }}"
},
"schema": [
{
"id": "Parution",
"type": "string",
"display": true,
"required": false,
"displayName": "Parution",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Objet",
"type": "string",
"display": true,
"required": false,
"displayName": "Objet",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "URL",
"type": "string",
"display": true,
"required": false,
"displayName": "URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Acheteur",
"type": "string",
"display": true,
"required": false,
"displayName": "Acheteur",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Date limite",
"type": "string",
"display": true,
"required": false,
"displayName": "Date limite",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Ok",
"type": "string",
"display": true,
"required": false,
"displayName": "Ok",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"row_number"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit#gid=0",
"cachedResultName": "All"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit?gid=0#gid=0"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "wBRLUCktxqXE6DVJ",
"name": "Google Sheets account"
}
},
"typeVersion": 4.7
},
{
"id": "08d027bd-ad2b-4f00-8fde-b1900aca6b7d",
"name": "更新偏移量",
"type": "n8n-nodes-base.googleSheets",
"position": [
2000,
-304
],
"parameters": {
"columns": {
"value": {
"Offset": "={{ $json.nextOffset }}",
"row_number": 2
},
"schema": [
{
"id": "Offset",
"type": "string",
"display": true,
"required": false,
"displayName": "Offset",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"row_number"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 862285542,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit#gid=862285542",
"cachedResultName": "Offset"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit?gid=862285542#gid=862285542"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "wBRLUCktxqXE6DVJ",
"name": "Google Sheets account"
}
},
"typeVersion": 4.7
},
{
"id": "6c7e50b2-c920-405b-9382-638ef5a4c620",
"name": "获取配置",
"type": "n8n-nodes-base.googleSheets",
"position": [
-624,
-304
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 966659321,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1wapLLWjwzo7SfG_YEsUlFaPRs1MmjxPRhRc6BlwBUAY/edit#gid=966659321",
"cachedResultName": "Config"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "https://docs.google.com/spreadsheets/d/1wapLLWjwzo7SfG_YEsUlFaPRs1MmjxPRhRc6BlwBUAY/edit?gid=966659321#gid=966659321"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "wBRLUCktxqXE6DVJ",
"name": "Google Sheets account"
}
},
"typeVersion": 4.7
},
{
"id": "049a5165-d7bb-4c82-a631-37c448c6f8d2",
"name": "便签1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1040,
560
],
"parameters": {
"color": 3,
"width": 4320,
"height": 1136,
"content": "# 大阶段 2:根据关键词过滤招标"
},
"typeVersion": 1
},
{
"id": "435daded-cb60-462a-bf29-5182beb34fa9",
"name": "便签",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1040,
-592
],
"parameters": {
"color": 5,
"width": 4320,
"height": 1088,
"content": "# 大阶段 1:使用配置获取所有招标"
},
"typeVersion": 1
},
{
"id": "4e23fa14-6253-4456-90d8-0b9f99cf873e",
"name": "便签2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-816,
-448
],
"parameters": {
"width": 480,
"height": 320,
"content": "# 阶段 1.1:获取配置"
},
"typeVersion": 1
},
{
"id": "3533cc22-225d-4e7e-b139-1db9c8266a91",
"name": "便签3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-304,
-448
],
"parameters": {
"width": 400,
"height": 320,
"content": "# 阶段 1.2:获取偏移量"
},
"typeVersion": 1
},
{
"id": "c08dcdb6-dad2-41c6-a3b1-c787662f0dbd",
"name": "便签4",
"type": "n8n-nodes-base.stickyNote",
"position": [
128,
-448
],
"parameters": {
"width": 416,
"height": 320,
"content": "# 阶段 1.3:获取招标(API 调用)"
},
"typeVersion": 1
},
{
"id": "265b9db5-c103-4fcf-943f-18f42908264f",
"name": "便签5",
"type": "n8n-nodes-base.stickyNote",
"position": [
576,
-448
],
"parameters": {
"width": 432,
"height": 320,
"content": "# 阶段 1.4:招标排序(市场类型过滤器)"
},
"typeVersion": 1
},
{
"id": "3a1ebce4-785d-4558-a5bf-5a2aeb94886e",
"name": "便签6",
"type": "n8n-nodes-base.stickyNote",
"position": [
1024,
-464
],
"parameters": {
"width": 624,
"height": 336,
"content": "# 阶段 1.5:将数据导入表格"
},
"typeVersion": 1
},
{
"id": "53cdf26f-baa7-4c10-9281-614134d12058",
"name": "便签7",
"type": "n8n-nodes-base.stickyNote",
"position": [
1664,
-464
],
"parameters": {
"width": 496,
"height": 336,
"content": "# 阶段 1.6:计算偏移量(分页)+ 更新偏移量"
},
"typeVersion": 1
},
{
"id": "180969ad-bedf-4f5f-8b9e-68a82b8dd883",
"name": "便签8",
"type": "n8n-nodes-base.stickyNote",
"position": [
2176,
-464
],
"parameters": {
"width": 496,
"height": 336,
"content": "# 阶段 1.7:偏移量完成了吗?(循环控制)"
},
"typeVersion": 1
},
{
"id": "e5342f99-2545-47c4-a80b-26d979cd7828",
"name": "便签9",
"type": "n8n-nodes-base.stickyNote",
"position": [
2704,
-464
],
"parameters": {
"width": 496,
"height": 336,
"content": "# 阶段 1.8:如果完成则重置偏移量"
},
"typeVersion": 1
},
{
"id": "3d9d0e75-5699-406d-9926-54d190ca4672",
"name": "计划触发器1",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-992,
-304
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 8 1 * *"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "59fcdbd5-2a44-472b-a6f0-24edcbc4ad74",
"name": "便签10",
"type": "n8n-nodes-base.stickyNote",
"position": [
-256,
816
],
"parameters": {
"width": 528,
"height": 304,
"content": "# 阶段 2.2:获取所有招标并过滤已处理的"
},
"typeVersion": 1
},
{
"id": "62d988fa-e756-4d2c-a4d4-7776af88ed67",
"name": "便利贴11",
"type": "n8n-nodes-base.stickyNote",
"position": [
336,
784
],
"parameters": {
"width": 624,
"height": 336,
"content": "# 阶段 2.3:循环遍历招标并提取内容"
},
"typeVersion": 1
},
{
"id": "93025880-cce5-49d9-b66b-06471a10665c",
"name": "便利贴12",
"type": "n8n-nodes-base.stickyNote",
"position": [
1024,
816
],
"parameters": {
"width": 512,
"height": 304,
"content": "# 阶段 2.4:分析招标内容(关键词匹配)"
},
"typeVersion": 1
},
{
"id": "cb5a8329-12a0-49f1-8a0e-04a55ce169f8",
"name": "获取关键词",
"type": "n8n-nodes-base.googleSheets",
"position": [
-608,
928
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 966659321,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1wapLLWjwzo7SfG_YEsUlFaPRs1MmjxPRhRc6BlwBUAY/edit#gid=966659321",
"cachedResultName": "Config"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "https://docs.google.com/spreadsheets/d/1wapLLWjwzo7SfG_YEsUlFaPRs1MmjxPRhRc6BlwBUAY/edit?gid=966659321#gid=966659321"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "wBRLUCktxqXE6DVJ",
"name": "Google Sheets account"
}
},
"typeVersion": 4.7
},
{
"id": "6d56dc34-92ae-4d30-96be-76123b9f1c46",
"name": "便签13",
"type": "n8n-nodes-base.stickyNote",
"position": [
-816,
816
],
"parameters": {
"width": 528,
"height": 304,
"content": "# 阶段 2.1:根据关键词过滤招标"
},
"typeVersion": 1
},
{
"id": "7881bd24-3287-4689-8e3c-6436807d030a",
"name": "便签15",
"type": "n8n-nodes-base.stickyNote",
"position": [
2112,
816
],
"parameters": {
"width": 576,
"height": 304,
"content": "# 阶段 2.6:将匹配的招标保存到目标表格"
},
"typeVersion": 1
},
{
"id": "f4302f99-e21f-4a95-836c-49e9b6cb03e7",
"name": "招标排序",
"type": "n8n-nodes-base.code",
"position": [
752,
-304
],
"parameters": {
"jsCode": "// Node code pour filtrer les appels d'offres selon les types définis dans la config (n8n)\n// Récupération de la configuration depuis le node \"Get config\"\nconst configData = $node[\"Get config\"].json;\nconsole.log(\"Configuration:\", JSON.stringify(configData, null, 2));\n\n// Extraction des types de marché activés (checkbox à true) depuis la config\nlet marketTypes = [];\n\nif (Array.isArray(configData)) {\n // Si c'est un tableau, on prend le premier élément\n const config = configData[0];\n if (config.Travaux === true) marketTypes.push(\"TRAVAUX\");\n if (config.Services === true) marketTypes.push(\"SERVICES\");\n if (config.Fournitures === true) marketTypes.push(\"FOURNITURES\");\n} else {\n // Si c'est un objet unique\n if (configData.Travaux === true) marketTypes.push(\"TRAVAUX\");\n if (configData.Services === true) marketTypes.push(\"SERVICES\");\n if (configData.Fournitures === true) marketTypes.push(\"FOURNITURES\");\n}\n\nconsole.log(\"Types de marché à filtrer:\", marketTypes);\n\n// Vérification qu'au moins un type est sélectionné\nif (marketTypes.length === 0) {\n throw new Error(\"Aucun type de marché sélectionné dans la configuration\");\n}\n\n// Récupération des données depuis l'input\nconst inputData = $input.all();\n\n// Debug : afficher la structure des données\nconsole.log(\"inputData:\", JSON.stringify(inputData, null, 2));\n\n// Vérification que les données sont bien structurées\nif (!inputData || inputData.length === 0) {\n throw new Error(\"Aucune donnée en entrée\");\n}\n\n// Récupération du premier item\nconst firstItem = inputData[0];\nconsole.log(\"firstItem:\", JSON.stringify(firstItem, null, 2));\n\nconst data = firstItem.json;\nconsole.log(\"data:\", JSON.stringify(data, null, 2));\n\n// La structure semble être directement un tableau avec total_count et results\nlet payload;\nif (Array.isArray(data)) {\n payload = data[0];\n} else if (data && data.total_count !== undefined && data.results) {\n payload = data;\n} else {\n throw new Error(\"Structure de données non reconnue\");\n}\n\nconsole.log(\"payload:\", JSON.stringify(payload, null, 2));\n\nif (!payload.results || !Array.isArray(payload.results)) {\n throw new Error(\"Structure de données invalide - results non trouvé\");\n}\n\n// Filtrage des résultats pour ne garder que les appels d'offres correspondant aux types de la config\nconst filteredResults = payload.results.filter(item => {\n if (!item.type_marche || !Array.isArray(item.type_marche)) {\n return false;\n }\n \n // Vérifie si au moins un des types de marché de l'item correspond à ceux de la config\n return item.type_marche.some(type => \n marketTypes.includes(type)\n );\n});\n\n// Construction du tableau de sortie avec la même structure\nconst output = [{\n total_count: payload.total_count, // Garde le total_count original\n filtered_count: filteredResults.length, // Ajoute le nombre filtré\n market_types_used: marketTypes, // Ajoute les types utilisés pour le filtre\n results: filteredResults\n}];\n\n// Log pour debug\nconsole.log(`Nombre d'appels d'offres filtrés: ${filteredResults.length} sur ${payload.total_count} total`);\nconsole.log(`Types de marché filtrés: ${marketTypes.join(', ')}`);\n\n// Retour du résultat\nreturn output;"
},
"typeVersion": 2
},
{
"id": "f54308af-1c9e-477a-bca2-4b9ede90d33b",
"name": "便签16",
"type": "n8n-nodes-base.stickyNote",
"position": [
-816,
-80
],
"parameters": {
"width": 480,
"height": 368,
"content": "### 系统功能:"
},
"typeVersion": 1
},
{
"id": "aca85321-0d84-4522-b21b-1500d709320d",
"name": "便签17",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1680,
-608
],
"parameters": {
"color": 4,
"width": 592,
"height": 1504,
"content": "# 阶段 0:初始配置设置 您需要做的:"
},
"typeVersion": 1
},
{
"id": "9ef13b1e-cfd7-4f58-90d7-3c6200b63fb5",
"name": "便签18",
"type": "n8n-nodes-base.stickyNote",
"position": [
-304,
-80
],
"parameters": {
"width": 400,
"height": 304,
"content": "### 系统功能:"
},
"typeVersion": 1
},
{
"id": "09ff5fd8-ecf2-49c4-a0a6-192a924c30dd",
"name": "便签19",
"type": "n8n-nodes-base.stickyNote",
"position": [
128,
-80
],
"parameters": {
"width": 416,
"height": 448,
"content": "### 系统功能:"
},
"typeVersion": 1
},
{
"id": "4a43bad3-a932-4d7e-a66c-be7e10aa83d1",
"name": "便签20",
"type": "n8n-nodes-base.stickyNote",
"position": [
576,
-80
],
"parameters": {
"width": 432,
"height": 352,
"content": "### 系统功能:"
},
"typeVersion": 1
},
{
"id": "ce64a66d-9cbb-41ad-8226-cfd6c2646f1f",
"name": "便签21",
"type": "n8n-nodes-base.stickyNote",
"position": [
1024,
-80
],
"parameters": {
"width": 624,
"height": 384,
"content": "### 系统功能:"
},
"typeVersion": 1
},
{
"id": "d721845b-d424-4671-8141-9db827f4b9cb",
"name": "便签22",
"type": "n8n-nodes-base.stickyNote",
"position": [
1664,
-80
],
"parameters": {
"width": 496,
"height": 368,
"content": "### 系统功能:"
},
"typeVersion": 1
},
{
"id": "a2fea90a-160c-4605-a08a-5ea12b9d6c88",
"name": "便签23",
"type": "n8n-nodes-base.stickyNote",
"position": [
2176,
-80
],
"parameters": {
"width": 496,
"height": 432,
"content": "### 系统功能:"
},
"typeVersion": 1
},
{
"id": "5d559d01-3175-49d5-a228-a087f3f082aa",
"name": "便签24",
"type": "n8n-nodes-base.stickyNote",
"position": [
2704,
-80
],
"parameters": {
"width": 496,
"height": 272,
"content": "### 系统功能:"
},
"typeVersion": 1
},
{
"id": "ce07b9ef-230d-49e5-8c41-6bea96b194cd",
"name": "便签 25",
"type": "n8n-nodes-base.stickyNote",
"position": [
-816,
1136
],
"parameters": {
"width": 528,
"height": 272,
"content": "### 系统功能:"
},
"typeVersion": 1
},
{
"id": "b041359a-cbee-462e-ba40-90c989cc527c",
"name": "便签 26",
"type": "n8n-nodes-base.stickyNote",
"position": [
-256,
1136
],
"parameters": {
"width": 528,
"height": 304,
"content": "### 系统功能:"
},
"typeVersion": 1
},
{
"id": "b0dae467-4f37-4d99-a70d-76c53f0302df",
"name": "便签27",
"type": "n8n-nodes-base.stickyNote",
"position": [
336,
1136
],
"parameters": {
"width": 624,
"height": 304,
"content": "### 系统功能:"
},
"typeVersion": 1
},
{
"id": "7e4da99d-ab37-4c14-83f0-1bfdaebfca5e",
"name": "便签28",
"type": "n8n-nodes-base.stickyNote",
"position": [
1024,
1136
],
"parameters": {
"width": 512,
"height": 448,
"content": "### 系统功能:"
},
"typeVersion": 1
},
{
"id": "63e86445-b28e-4695-9d64-cc23e786cfe9",
"name": "便签14",
"type": "n8n-nodes-base.stickyNote",
"position": [
1568,
816
],
"parameters": {
"width": 512,
"height": 304,
"content": "# 阶段 2.5:将招标标记为已处理"
},
"typeVersion": 1
},
{
"id": "cf19c558-d1c0-4643-8bf4-c1dbcc3c8305",
"name": "便签29",
"type": "n8n-nodes-base.stickyNote",
"position": [
1568,
1136
],
"parameters": {
"width": 512,
"height": 288,
"content": "### 系统功能:"
},
"typeVersion": 1
},
{
"id": "0d1d396d-9c99-4cfb-b70d-222673eb5cd2",
"name": "便签30",
"type": "n8n-nodes-base.stickyNote",
"position": [
2112,
1136
],
"parameters": {
"width": 576,
"height": 432,
"content": "### 系统功能:"
},
"typeVersion": 1
}
],
"pinData": {},
"connections": {
"Ok": {
"main": [
[
{
"node": "Query match ?",
"type": "main",
"index": 0
}
]
]
},
"Wait": {
"main": [
[
{
"node": "Get Offset",
"type": "main",
"index": 0
}
]
]
},
"Wait1": {
"main": [
[
{
"node": "Loop Over offres",
"type": "main",
"index": 0
}
]
]
},
"Filter": {
"main": [
[
{
"node": "Loop Over offres",
"type": "main",
"index": 0
}
]
]
},
"Get All": {
"main": [
[
{
"node": "Filter",
"type": "main",
"index": 0
}
]
]
},
"Get query": {
"main": [
[
{
"node": "Ok",
"type": "main",
"index": 0
}
]
]
},
"Get Offset": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"Get config": {
"main": [
[
{
"node": "Get Offset",
"type": "main",
"index": 0
}
]
]
},
"Get keyword": {
"main": [
[
{
"node": "Get All",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request": {
"main": [
[
{
"node": "Tenders sorting",
"type": "main",
"index": 0
}
]
]
},
"Target offre": {
"main": [
[
{
"node": "Loop Over offres",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request4": {
"main": [
[
{
"node": "Extract from File",
"type": "main",
"index": 0
}
]
]
},
"Query match ?": {
"main": [
[
{
"node": "Target offre",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait1",
"type": "main",
"index": 0
}
]
]
},
"Update offset": {
"main": [
[
{
"node": "Check Continue Loop",
"type": "main",
"index": 0
}
]
]
},
"Format Results": {
"main": [
[
{
"node": "Append row in sheet",
"type": "main",
"index": 0
}
]
]
},
"Tenders sorting": {
"main": [
[
{
"node": "Format Results",
"type": "main",
"index": 0
},
{
"node": "Process Response",
"type": "main",
"index": 0
}
]
]
},
"Loop Over offres": {
"main": [
[],
[
{
"node": "HTTP Request4",
"type": "main",
"index": 0
}
]
]
},
"Process Response": {
"main": [
[
{
"node": "Update offset",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Get keyword",
"type": "main",
"index": 0
}
]
]
},
"Extract from File": {
"main": [
[
{
"node": "Get query",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger1": {
"main": [
[
{
"node": "Get config",
"type": "main",
"index": 0
}
]
]
},
"Check Continue Loop": {
"main": [
[
{
"node": "Wait",
"type": "main",
"index": 0
}
],
[
{
"node": "Reset Offset",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级 - 内容创作, 多模态 AI
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
使用Claude AI和竞品分析的SEO内容生成器
使用Claude AI和Apify竞品分析生成SEO内容
If
Code
Filter
+9
36 节点Growth AI
内容创作
使用Claude AI、竞品分析和Supabase RAG的SEO内容生成器
使用Claude AI、竞品分析和Supabase RAG生成SEO内容
If
Code
Filter
+11
40 节点Growth AI
内容创作
WordPress博客自动化专业版(深度研究)v2.1市场
使用GPT-4o、Perplexity AI和多语言支持自动化SEO优化的博客创建
If
Set
Xml
+27
125 节点Daniel Ng
内容创作
LinkedIn和X病毒内容自动引擎
使用AI生成和发布自动创建LinkedIn和X的病毒内容
If
Set
Wait
+26
156 节点Diptamoy Barman
内容创作
使用AI和Freepik将Reddit商业痛点转换为病毒式LinkedIn内容
使用AI和Freepik将Reddit商业痛点转换为病毒式LinkedIn内容
If
Set
Code
+16
48 节点Daniel Lianes
内容创作
使用Claude AI从Google Sheets到WordPress生成无障碍替代文本
使用Claude AI从Google Sheets到WordPress生成无障碍替代文本
If
Code
Http Request
+5
10 节点Growth AI
内容创作
工作流信息
难度等级
高级
节点数量55
分类2
节点类型10
作者
Growth AI
@growthain8n automation expert eliminating repetitive tasks for businesses. Specializing in native API integrations (HubSpot, Pipedrive, Notion, Gmail, Slack) with custom workflows tailored to your tech stack. GDPR-compliant European hosting with AI agents. From lead qualification to content generation - I architect battle-tested automations that run 24/7. Your manual routines disappear without pain.
外部链接
在 n8n.io 查看 →
分享此工作流