如何在不預先指定列名的情況下動態取得 Google 試算表中的任何資料?

我上傳了不同類型的 CSV 檔案,這些檔案有不同的欄位名稱,通過 webhook 進入我的 n8n 工作流程,然後我使用「Extract from CSV」節點將資料轉換成 JSON 格式,現在我困惑於如何在不指定欄位名稱的情況下將這些資料放入我的 Google 試算表。我希望 Google 試算表能自動偵測欄位數量和欄位名稱,這有可能嗎?

嘿 Abdullah!:waving_hand:

這是一個非常常見的問題。Google Sheets 節點中的標準「附加行」操作(使用「自動對應」)實際上要求標頭已經存在於您工作表的第 1 列中,這樣它才能知道哪一列對應於哪個 JSON 鍵。它無法在完全空白的現有工作表上使用標準附加節點魔法般地建立標頭。

但是,有兩種優雅的解決方案:

選項 1:動態建立新工作表(最簡單)

不要附加到現有的空白工作表,而是使用 Google Sheets 節點並設定:

• 資源:工作表
• 操作:建立
如果您將 JSON 資料傳遞到此節點,n8n 將自動在您的 Google 文件中建立一個全新的標籤頁(工作表)。它會智慧地讀取 JSON 中的所有鍵,自動將它們寫入為第 1 列中的標頭,並在其下方插入所有 CSV 資料。非常適合動態 CSV!

選項 2:Google Sheets API(針對現有的空白工作表)

如果您必須將資料推送到特定的、已經存在的空白工作表中,原生 n8n 節點會遇到困難。您必須使用 HTTP 請求節點直接與 Google Sheets API 通訊(values:append 端點)。
您會先使用程式碼節點將 JSON 陣列轉換為 2D 陣列(陣列的陣列),其中第一個陣列包含您提取的 Object.keys()。然後您透過 HTTP 請求節點將此原始 2D 陣列傳送給 Google。

如果您的工作流程允許,我強烈建議使用選項 1。只需根據 CSV 檔案名稱或當前日期動態建立新工作表!

希望這對您有幫助 :smiley:

@Abdullah_Shah2

我想問你一個問題。你有多個不同的 CSV 檔案,每個都有不同的欄位,然後你想要 upsert 到同一個工作表?工作表是一個基本的資料庫,標題列就是結構描述,要進行 upsert 的話,你必須在這些欄位上進行匹配。

所以我的第一個問題是,資料是相同類型的資料但有不同的標題?(例如 EMAIL、email_address、E-Mail)或者它們根本是完全不同的資料集?

Michael 的問題是關鍵。一個工作表只能有一個標題列,所以「將動態欄位整合到一個現有工作表」是結構描述問題,不是節點問題——沒有任何節點設定能解決它。

有兩個誠實的做法:如果每個 CSV 確實是不同的資料集,yoel 的選項 1(為每個檔案建立新分頁)是正確的做法。如果是相同的資料但標題名稱混亂(email vs E-Mail vs email_address),先在一個小的 Code 節點中標準化金鑰,手動在第 1 列寫入你的標準標題,從那之後使用自動對應的 Append 就能正常運作——它會忽略多餘的金鑰,只填補相符的部分。第二種設定方式可以應對新的 CSV 變體,而不需要再次接觸工作表。

@Abdullah_Shah2

是的,這完全可行!

根據預設,Google Sheets 節點期望你的工作表的第 1 列已經包含預先定義的標題名稱。由於你的 CSV 檔案有動態/變化的欄名,訣竅是使用程式碼節點從 CSV 資料動態提取標題,並將所有內容格式化為 2D 陣列(其中第 1 列 = 動態標題,第 2 列以後 = 值)。

以下是確切的 3 步工作流程設定:

步驟 1:從 CSV 節點提取

保持你現有的從 CSV 提取節點不變。

步驟 2:新增程式碼節點(Google Sheets 格式化)

在你的「從 CSV 提取」節點之後連接一個程式碼節點(模式:針對所有項目執行一次),並貼上這個 JavaScript 程式碼:

const items = $input.all();
if (items.length === 0) return [];

const headers = Object.keys(items[0].json);

const values = [headers];

for (const item of items) {
  const row = headers.map(header => item.json[header] ?? '');
  values.push(row);
}

return [{ json: { values } }];

步驟 3:Google Sheets 節點設定

在你的 Google Sheets 節點中,按如下方式進行設定:

  • **資源:**工作表(或儲存格)
  • **操作:**更新(或「清除後更新」,如果你想覆寫先前的資料)
  • **資料模式:**自動對應或在下方定義
  • **範圍:**A1
  • **欄位 / 值:**對應 {{ $json.values }}

工作原理:

  1. Object.keys() 會自動讀取 CSV 承載中存在的任何欄名。
  2. 它將這些標題名稱直接寫入 Google Sheets 的第 1 列(A1、B1、C1…)。
  3. 它在動態標題下方填入所有資料列。

如果你在設定時遇到任何問題,請告訴我!

謝謝!

上面好答案的一個值得補充的地方:無論你選擇哪種方式,都要將原始 CSV 鍵和你的工作表標題保持在分開的步驟中,並且記錄每個檔案看到的標題集。實際上問題不在第一次匯入,而是第十二個檔案出現時,用的是「Email Address」而不是「email」,然後無聲地落在空白欄位裡。

一個對我來說一直很管用的廉價模式:一個 Code 節點建立一個標準化的鍵映射(小寫、去除空格/標點符號),用 Append + 自動映射將規範欄位寫到你的主標籤,任何未映射的鍵都進入第二個「未匹配」標籤,並附上檔案名稱和時間戳。這樣你就能看到新標題變體的可見佇列,而不是無聲的資料遺失,添加變體只需在映射中改一行,而不需要重建工作表。

如果你的 CSV 真的是不同的資料集,每個來源一個標籤(或工作表)加上一個小索引標籤指向它們,比試圖強行使用一個架構要好得多。

謝謝 — Reed

方法是對的,但標頭行中有一個錯誤,會直接影響你所描述的情況。

`const headers = Object.keys(items[0].json);`

這只會從第一行讀取欄位名稱。如果任何後續行包含第一行沒有的鍵,該欄位會被無聲地丟棄。由於你的整個問題是欄位在檔案之間會變化,這正是它失敗的情況,而且會無聲地失敗。改為取所有行的聯集:

`const headers = […new Set(items.flatMap(i => Object.keys(i.json)))];`

還有另外兩件值得處理的事情。

如果任何值是物件或陣列(當 CSV 在儲存格中包含巢狀 JSON 時就會發生),`?? ‘’` 的備用值會直接通過,Sheets 會寫入 [object Object]。先用 JSON.stringify 強制轉換任何非基本類型。

現在就決定類型強制轉換,而不是等到它無聲地破壞了什麼。Google Sheets 節點預設會讓 Sheets 像某人輸入值一樣解釋值,所以任何看起來像 1-2 或 3/4 或 SEP 5 的東西在寫入時都會變成日期,之後重新格式化儲存格也無法撤銷。使用動態欄位時,你無法預測哪些欄位會看起來像日期,所以除非你確實想要該解析,否則將節點的儲存格格式選項設定為 RAW。