Sheets 自動化:表單進來,報表自己長好
你用 Google Form 做了一份回饋問卷,活動結束後表單積了 83 筆回應。打開連結的 Sheets,看到那 83 列密密麻麻的自由填答——有人寫「還不錯」,有人寫「等太久了啦」,有人空著。你想統計「多少人負面」,但自由填答沒辦法直接計算。最後你複製幾句到報告裡,打上「整體反應正面」,把視窗關掉。那 83 筆資料就在那邊,沒有真正被讀過。
癥結不是你懶,是流程缺了自動化。表單資料進 Sheets 這件事 Google 幫你做好了;但「資料進來以後的處理」,Google 原生功能只能靠你手動。這堂課用 n8n 把後半段接起來:資料進來 → 觸發流程 → AI 清洗分類 → 統計寫回 Sheets → 週報自動寄到你信箱。你只管看報告。
這堂學什麼
- Google Sheets OAuth2 設定:啟用 Sheets API、建憑證、設定 Redirect URI——跟第 3 課的 Gmail 操作同框架,本課重點在差異與常見錯誤
- Form → Sheets Trigger 觸發鏈:為什麼 n8n 無法直接觸發 Google Form、Sheets Trigger 的輪詢機制怎麼運作
- AI 資料清洗:用 n8n 的 Text Classifier 節點把自由填答自動轉成標準分類,清洗結果寫回同一張 Sheets
- 自動週報:每週一用 Schedule Trigger 讀 Sheets 資料、AI 生成摘要、Gmail 寄出週報——全自動
- Sheets 當輕量資料庫:三表分工設計(原始資料 / FAQ 知識庫 / 統計彙整),呼應 LINE OA 課 FAQ 表的結構
觀念一:為什麼不能直接觸發 Form
很多人第一次想做這件事,會去找「Google Form Trigger」——n8n 裡沒有這個節點,不是 n8n 的問題,是 Google Form 本身沒有 Webhook 機制。
真正可以觸發的是 Google Sheets Trigger。做法是:先在 Google Form 後台把「回應」連結到一張 Sheets(Form 內建功能,不用 n8n),以後每筆新回應會自動出現在那張 Sheets 的最後一列;n8n 的 Sheets Trigger 再定時檢查有沒有新列,有的話就觸發流程。

這條觸發鏈有一個特性要先知道:Sheets Trigger 是輪詢(polling),不是即時。預設每分鐘跑一次,填完表到 n8n 收到通知,中間最多延遲 1 分鐘。對回饋表單、報名表這完全可以接受;需要秒級即時的情境才需要改用 Apps Script Webhook,但那要寫程式碼,本課不展開。
第一關:Sheets OAuth2 設定
第 3 課已設定過 Gmail OAuth2,整個流程你應該有點印象。Sheets 的設定框架完全一樣,差別只在兩個地方:啟用的 API 換成 Google Sheets API,以及憑證類型選「Google Sheets OAuth2」。
如果你已在第 3 課建好 Google Cloud 專案,可以直接沿用同一個專案,追加啟用 Sheets API 即可;Redirect URI 不用重設,Sheets 和 Gmail 可以共用同一組 OAuth 用戶端。

建立 Google Cloud 專案並啟用 Sheets API
到 console.cloud.google.com,如果第 3 課已建專案,從頂部選到那個專案即可。若是全新開始:
- 點「選取專案」→「新增專案」,名稱填
n8n-google-auto - 建好後,左側選「APIs 和服務」→「程式庫」
- 搜尋
Google Sheets API,點進去按「啟用」
接著確認 OAuth 同意畫面已設定(第 3 課做過就不用重做)。如果是第一次,步驟同第 3 課:使用者類型選「外部」,把你自己的 Google 帳號加入測試使用者清單。
建立 OAuth 用戶端,取得 Client ID 和 Client Secret
左側選「憑證」→「+ 建立憑證」→「OAuth 用戶端 ID」:
- 應用程式類型:網頁應用程式
- 名稱:
n8n(或任意名稱) - 按「建立」
跳出的對話框把 Client ID 和 Client Secret 都複製下來,對話框關掉就看不到 Secret 了。
在 n8n 建立憑證,貼回 Redirect URI
打開 n8n,左側選「Credentials」→「Add Credential」,搜尋選「Google Sheets OAuth2 API」:
- 把剛才的 Client ID 和 Client Secret 貼入
- n8n 頁面下方顯示的 OAuth Redirect URL 複製起來
- 回 Google Cloud Console → 憑證,找到剛建的 OAuth 用戶端,點編輯
- 「已授權的重新導向 URI」加入剛才複製的 Redirect URL,存檔
回到 n8n,點「Sign in with Google」完成授權。看到「Connection tested successfully」就完成了。
流程①:Form 接 Sheets,Sheets 觸發 n8n,AI 清洗資料
先讓 Google Form 連結到 Sheets
打開你的 Google Form(任何一個已有填答的,或新建一個測試用),頂部點「回應」分頁,右側有一個 Sheets 圖示按鈕「在試算表中查看回應」,按下去選「建立新的試算表」,Google 會自動建立一張連結好的 Sheets,之後每筆新填答都會自動附加一列。
建立 n8n 工作流
新增 Google Sheets Trigger 節點
在 n8n 建立新的工作流,第一個節點選「Google Sheets Trigger」:
- Credential:選剛設定好的 Google Sheets OAuth2
- Event:Row Added(有新列加入時觸發)
- Document:點選你的試算表名稱(會自動列出有權限的所有 Sheets)
- Sheet Name:選「Form Responses 1」(Google Form 自動建立的工作表名稱)
- Poll Times:預設 Every Minute(每分鐘輪詢一次)
存檔後按「Test workflow」,到 Google Form 送一筆測試填答,等最多 1 分鐘,看 Sheets Trigger 有沒有攔截到這筆新列的資料。成功的話,右側輸出欄位會列出所有問題欄位名稱和你剛填的值。
加入 Text Classifier 節點做 AI 分類
接在 Sheets Trigger 後面,加入「Text Classifier」節點(在 n8n 節點搜尋「Text Classifier」,它在 Langchain 分類下):
- Text:填運算式,對應你 Form 裡的自由填答欄位,例如:
{{ $json["您的回饋意見"] }}
- Categories:加入以下分類(名稱 + 描述):
| 名稱 | 描述 |
|---|---|
| 正向 | 回應內容整體肯定、滿意或讚美的留言 |
| 負向 | 回應包含抱怨、不滿、要求改進或評分偏低的內容 |
| 中立 | 沒有明顯情緒傾向、敘述性回應、或空白 |
| 痛點:等待 | 提及等待時間過長、速度慢、太慢等相關內容 |
| 痛點:品質 | 提及品質差、有問題、損壞、不符合預期等 |
| 痛點:價格 | 提及太貴、價格偏高、CP值低等 |
| 痛點:服務 | 提及服務態度差、客服慢、處理不佳等 |
- Allow Multiple Classes Per Item:開啟(一筆回應可能同時是「負向」又是「痛點:等待」)

Text Classifier 節點需要連接一個 AI 模型:在節點下方的「Model」連接點,加入「OpenAI Chat Model」(或 Gemini 等你已設定的模型),Text Classifier 會把文字丟給這個模型判斷。
Set 節點:整理分類結果
Text Classifier 輸出的分類結果是一個陣列,例如 ["負向", "痛點:等待"]。加入「Set」節點把它整理成好寫回 Sheets 的格式:
// 在 Set 節點的 JavaScript 欄位使用以下邏輯
// 或用 Code 節點直接處理
const classes = $json.classes; // Text Classifier 輸出的分類陣列
// 情緒類別:取第一個非痛點的分類
const sentiment = classes.find(c => ['正向','負向','中立'].includes(c)) || '中立';
// 痛點標籤:把所有痛點類型合併
const painPoints = classes
.filter(c => c.startsWith('痛點:'))
.map(c => c.replace('痛點:', ''))
.join('、') || '無';
return [{ json: { sentiment, painPoints } }];
Google Sheets 節點:把分類寫回同一列
這一步是把 AI 分析結果寫回原本的那筆 Sheets 資料。先在 Sheets 手動新增兩個欄位標題:AI_情緒 和 AI_痛點(在第一列的最右邊追加)。
加入「Google Sheets」節點:
- Operation:Append or Update Row(不是 Append Row——Append 會新增一列,你要的是更新原本那列)
- Document / Sheet:和 Trigger 同一張
- Matching Columns:選「時間戳記」(Timestamp)欄位作為識別鍵,確保更新的是正確那一列
- Columns to Send:
AI_情緒→{{ $json.sentiment }}AI_痛點→{{ $json.painPoints }}
整條流程串好:Sheets Trigger → Text Classifier → Set → Google Sheets(更新列)。啟動後,每次有人填 Form,最多一分鐘後 Sheets 裡那筆資料的 AI_情緒 和 AI_痛點 欄位就會自動填好。
流程②:自動週報
每週一早上自動讀 Sheets 資料,AI 生成上週回饋摘要,寄到你的 Gmail。

Schedule Trigger:每週一早上九點
新建一條工作流,第一個節點選「Schedule Trigger」:
- Mode:Cron
- Cron Expression:
0 9 * * 1(每週一早上 09:00) - Timezone:Asia/Taipei
Google Sheets 節點:讀本週資料
加入「Google Sheets」節點:
- Operation:Get Many Rows
- Document / Sheet:回應試算表
- Filters:這裡可以用時間篩選,但 Sheets 節點不支援直接按日期過濾;先抓全部資料,再在後面的 Code 節點裡用 JavaScript 過濾最近 7 天。
Limit 先填 500(超過 500 筆週回應時,需改用分批彙整,屬進階情境,先不展開)。
Code 節點:過濾本週資料並計算統計
// 取得今天和7天前的時間
const now = new Date();
const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
// 過濾本週資料(時間戳記欄位名稱依你的 Form 設定調整)
const allRows = $input.all();
const weekRows = allRows.filter(item => {
const ts = new Date(item.json['時間戳記']);
return ts >= sevenDaysAgo && ts <= now;
});
// 統計情緒分類
const sentimentCount = { 正向: 0, 中立: 0, 負向: 0 };
const painPointCount = {};
weekRows.forEach(item => {
const s = item.json['AI_情緒'] || '中立';
if (sentimentCount[s] !== undefined) sentimentCount[s]++;
const pp = item.json['AI_痛點'];
if (pp && pp !== '無') {
pp.split('、').forEach(p => {
painPointCount[p] = (painPointCount[p] || 0) + 1;
});
}
});
// 痛點排行
const topPainPoints = Object.entries(painPointCount)
.sort((a, b) => b[1] - a[1])
.slice(0, 3)
.map(([k, v]) => `${k}(${v}次)`)
.join('、') || '本週無明顯痛點';
// 挑出代表原始留言(正面和負面各一句)
const findSample = (sentiment) => {
const item = weekRows.find(r => r.json['AI_情緒'] === sentiment && r.json['您的回饋意見']);
return item ? `「${item.json['您的回饋意見'].slice(0, 50)}」` : '(無代表留言)';
};
return [{
json: {
total: weekRows.length,
sentimentCount,
topPainPoints,
positiveSample: findSample('正向'),
negativeSample: findSample('負向'),
weekLabel: `${sevenDaysAgo.toLocaleDateString('zh-TW')} ~ ${now.toLocaleDateString('zh-TW')}`
}
}];
Basic LLM Chain:讓 AI 寫摘要和建議
加入「Basic LLM Chain」節點,連接你的 AI 模型(OpenAI 或 Google Gemini):
System Prompt:
你是一位客戶體驗分析師,請用繁體中文、專業但口語的風格,根據以下數字寫一份簡短的週報摘要,不要加多餘的標題格式,直接寫成自然段落(3-5 句),最後另起一段給出 1-2 條具體改善建議。
User Message:
本週回收回饋筆數: {{ $json.total }}
情緒分布: 正向 {{ $json.sentimentCount.正向 }} 筆、中立 {{ $json.sentimentCount.中立 }} 筆、負向 {{ $json.sentimentCount.負向 }} 筆
主要痛點: {{ $json.topPainPoints }}
正面代表留言: {{ $json.positiveSample }}
負面代表留言: {{ $json.negativeSample }}
統計週期: {{ $json.weekLabel }}
Gmail 節點:寄出週報
確認你已建好 Gmail OAuth2 憑證(第 3 課)。加入「Gmail」節點:
- Resource:Message
- Operation:Send
- To:
你的信箱@gmail.com(或團隊信箱) - Subject:
📊 本週回饋摘要 — {{ $('Code').item.json.weekLabel }} - Message Type:HTML
- Message:
<h2>本週回饋摘要</h2>
<p><strong>統計週期:</strong>{{ $('Code').item.json.weekLabel }}</p>
<p><strong>總填答數:</strong>{{ $('Code').item.json.total }} 筆</p>
<h3>情緒分布</h3>
<ul>
<li>正向:{{ $('Code').item.json.sentimentCount.正向 }} 筆</li>
<li>中立:{{ $('Code').item.json.sentimentCount.中立 }} 筆</li>
<li>負向:{{ $('Code').item.json.sentimentCount.負向 }} 筆</li>
</ul>
<h3>AI 摘要與建議</h3>
<p>{{ $json.text }}</p>
<hr>
<p style="color:gray;font-size:12px">由 n8n Google Sheets 自動化生成 · 如需查看原始資料請至 Sheets</p>
儲存並啟動。第一次先把 Schedule Trigger 換成 Manual Trigger 手動測一次,確認信件格式正常再換回定時觸發。
Sheets 當輕量資料庫
把 Sheets 只當「表單收集工具」太浪費了。用好三表分工,Sheets 可以撐起相當多的自動化情境,也是 LINE OA 課 FAQ 表的基礎結構。

三張工作表的分工:
工作表 1「原始回應」:Form 連結到這裡,每筆原始資料都在,n8n 的 AI 清洗結果也寫回這裡(AI_情緒、AI_痛點 兩個欄位)。這張表只有新增,不刪,是系統的事實來源。
工作表 2「FAQ 知識庫」:欄位固定為 ID / 關鍵字 / 標準回覆。n8n 在 LINE OA 課的流程會讀這張表——收到問題先比對關鍵字,有對應 FAQ 就回標準答覆,找不到才送 AI 生成。這張表由你手動維護。
工作表 3「統計」:放公式,不放原始資料。用 COUNTIF 統計各情緒分類的筆數,用 COUNTIFS 做多條件統計(例如本週且負向)。公式指向工作表 1,資料更新後統計自動刷新,不需要 n8n 介入。
幾個用 Sheets 當資料庫的實用原則:
- 第一列永遠是欄位標題,不要合併儲存格,不要讓第一列留空
- AI 寫入欄統一加前綴
AI_,一眼就能分辨哪些是原始回應、哪些是系統加工 - n8n 的識別鍵選「時間戳記」欄位,Google Form 自動填這欄,值通常唯一,適合當 key 來做 Append or Update Row
- 資料量超過 5,000 列時考慮換真實資料庫(PostgreSQL/Supabase),Sheets 輪詢效能到這個量級會明顯下降
Google 內建 Gemini vs n8n 路線
2026 年 Google Workspace 已整入 Gemini:Sheets 右上角的 Ask Gemini 側欄可用自然語言描述規則產生公式、批次填欄位(fill a range),也能叫 Gemini 直接建樞紐分析表或圖表,適合一次性的整批清洗。n8n 的優勢在持續自動:每次有新填答就自動跑,不需要手動觸發。兩個工具不衝突,一次性的整批清洗用內建 Gemini,定期持續自動化用 n8n。
常見坑
坑 1:Sheets Trigger 一直沒觸發,但 Form 確實有新填答
症狀:你填了表,等了兩分鐘,n8n 還是無反應。
先確認兩件事:一,Google Form 的「回應」分頁右上角有沒有出現 Sheets 圖示(如果沒出現,代表 Form 尚未連結任何 Sheets,點圖示新建連結)。二,到 n8n 這條工作流的「Executions」頁面看看有沒有執行記錄——有的話表示 Trigger 有跑,只是沒抓到資料,可能是 Sheets 欄位名稱對不上;沒有執行記錄,表示工作流沒有被啟動,確認右上角「Active」開關是否打開。
坑 2:OAuth 授權彈跳視窗出現「這個應用程式未通過 Google 驗證」
這個應用程式尚未通過 Google 驗證
您嘗試存取的應用程式尚未通過 Google 的安全審核。
這不是錯誤,是警告。OAuth 同意畫面在「測試中」狀態時 Google 會顯示這個提示。點「Advanced」→「Go to n8n (unsafe)」繼續即可,你是自己的應用連自己的帳號,完全沒問題。個人自用建議就留在測試狀態,不需要特別升成正式版。
坑 3:redirect_uri_mismatch 錯誤
Error 400: redirect_uri_mismatch
The redirect URI in the request did not match a registered redirect URI.
與第 3 課 Gmail 一樣:Google Cloud Console 裡的 Redirect URI 必須和 n8n 憑證頁面顯示的 URL 完全一致,多一個斜線或 http/https 不對都不行。從 n8n 複製貼上,不要手打。沿用第 3 課 OAuth 用戶端時,也要確認 URI 清單裡有對應這個 Sheets 憑證的 URL。
坑 4:AI 分類寫回 Sheets 後,資料變成兩列(Append 用錯了)
症狀:每次流程跑完,Sheets 的最後面多了一列只有 AI_情緒 和 AI_痛點 的新列,原始回應那列沒有被更新。
原因:Google Sheets 節點的 Operation 選了「Append Row」,這個操作是「在最後追加新列」。應該改成「Append or Update Row」,並在「Matching Columns」設定識別鍵(時間戳記欄位),它才會找到原本那列更新,而不是新增。
坑 5:Code 節點的日期過濾結果永遠是空的
症狀:週報跑完 total 是 0,信裡什麼都沒有。
最常見原因是時間戳記格式不是 JavaScript 能直接解析的格式。Google Form 寫入 Sheets 的格式通常是 2026/7/4 上午10:23:45(繁中系統),new Date() 對這種格式解析可能失敗,你需要先轉換:
// 如果時間戳記格式是 "2026/7/4 上午10:23:45"
// 先統一轉成標準格式
function parseTimestamp(ts) {
if (!ts) return new Date(0);
// 把 "上午" / "下午" 移除,改用 24 小時制
let cleaned = ts
.replace('上午', 'AM')
.replace('下午', 'PM')
.replace(/\//g, '-');
return new Date(cleaned);
}
const ts = parseTimestamp(item.json['時間戳記']);
先在 Code 節點裡印出幾筆時間戳記的實際值(用 console.log),確認格式再調整解析邏輯。
作業
- 必做:完成 Google Sheets OAuth2 設定,在 n8n 建一個只有 Sheets Trigger 的工作流,確認它能正確攔截 Google Form 新填答(哪怕後面什麼都沒接)。這一步通了,其他節點都是積木組裝。
- 必做:把流程①(Form → AI 分類 → 寫回 Sheets)完整跑起來。填 5 筆測試回應,觀察 Sheets 裡的
AI_情緒欄位被正確填入。如果有分類錯誤,回去調整 Text Classifier 裡那個分類的「描述」欄位——描述越清楚,分類越準。 - 必做:在你的 Sheets 手動新增第三張工作表「統計」,用
=COUNTIF(原始回應!D:D,"正向")這個公式驗證你的資料通了沒(注意工作表名稱替換成你的實際名稱)。看到計算結果出來,代表三表結構建對了。 - 選做:把流程②(週報)用 Manual Trigger 手動跑一次,看週報信件格式。如果 AI 摘要語氣或格式不對,修改 System Prompt 直到你滿意。修好了再換回 Schedule Trigger 設定每週一早上自動寄。
下一課預告
Sheets 搞定,你手上已有一張會自動分類、自動統計的資料表。下一個問題:工作上最耗時的事情往往不是資料整理,而是「會議」——開會前要收集資料、準備議題;開完要整理紀錄、追蹤待辦。第 5 課「日曆與文件:會議前自動準備、會議後自動記錄」,我們串接 Google Calendar 和 Google Docs,讓 n8n 在會議前半小時自動拉出相關資料、會議結束後根據你的筆記自動整理會議紀錄發給所有人。你的開會方式,從此不一樣。