當防爬蟲遇上書籤:一個技術成功但需求失敗的股票自動化專案
開場:家人的需求,工程師的條件反射
姊夫是股市老手,研究周轉率和 K 線圖形態已經好幾年了。某天他丟了個問題給我:「寫個自動化的程式把股票網站上的資料抓下來存到底行不行?我問了 Gemini 跟 ChatGPT,他們都說可以,但最後都卡住。」
聽到「自動化」三個字,我的工程師魂立刻燃起來了,儘管我對股市的理解大概停留在「紅的漲綠的跌」(還是反過來?)的程度。但沒關係,技術人嘛,不懂業務也能硬幹技術,對吧?
對吧?
挑戰一:防爬蟲的高牆
姊夫要的資料來自某個股票資訊網站的「周轉率排行榜」,每天前 100 支股票。一開始我就知道這種網站八成有防爬蟲機制,但心想「應該有辦法繞過去吧」,身為工程師,對這種事總是有種莫名的自信。
打開瀏覽器開發者工具,看到漂亮的表格資料,心想「不就是個 table 嘛,requests.get() 抓下來 parse 一下就好」。然後我寫了個 Python script,送出 request,拿回 HTML,
空的。
不是「資料格式不對」的那種空,是「整個 #rankingData 根本不存在」的那種空。
原來這網站用的是 CSR(Client-Side Rendering),所有資料都是前端 JavaScript 動態載入的。傳統爬蟲拿到的只是一個空殼 HTML,真正的資料藏在瀏覽器跑完 JS 之後才會出現。
好吧,那我用 Selenium 或 Playwright 模擬瀏覽器總行了吧?理論上可以,但這網站前面還擋了一層 Cloudflare Challenge,直接用 curl 或 requests 打過去,連 HTML 都拿不到,直接吃一個 403。就算用 headless browser,也得過 Cloudflare 那關,搞起來不見得划算。
這時候我想起了一個古老但有效的技巧:Bookmarklet。
解法:讓人類瀏覽器當爬蟲
Bookmarklet 的邏輯超簡單,既然網站防的是「機器人」,那我就讓「真人」去開網頁,然後用一個書籤小工具從已經渲染好的頁面上把資料抓下來。
運作流程是這樣的:
- 人工操作:我(或姊夫)手動開啟目標網站,網頁正常載入、通過所有驗證
- 點擊書籤:觸發一段預先寫好的 JavaScript
- 抓取 DOM:從已渲染的
#rankingData表格中提取資料(交易日期、股票代碼、名稱、周轉率、成交量、收盤價、漲跌幅) - POST 到 webhook:用
fetch()把 JSON 資料丟到 n8n 的 webhook endpoint
關鍵是這行:
fetch('https://your-n8n-instance.com/webhook/stock-data', {
method: 'POST',
mode: 'no-cors',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(stockData)
});
mode: 'no-cors' 讓瀏覽器不會因為跨域問題擋住請求,反正我們只要「送出去」就好,不需要讀取 response。
這招的美妙之處在於:防爬蟲機制完全繞過了,因為從網站的角度來看,這就是一個正常的使用者在正常地瀏覽網頁。
資料流:GAS → n8n → PostgreSQL → AI Agent
資料抓到之後,接下來是整條自動化流程:
第一站:Google Apps Script(GAS)
我原本打算直接讓 Bookmarklet 打到 n8n,但後來想想,Google Sheet 當中繼站有幾個好處:
- 姊夫可以直接看到原始資料(他不懂 PostgreSQL 但會用 Excel)
- 萬一後面的流程炸掉,至少資料還在
- 可以手動調整或補資料
所以寫了個簡單的 doPost(e) Cloud Function,接收 JSON 後寫入 Google Sheet。每筆資料包含:日期、排名、股票代碼、名稱、成交價、漲跌幅、成交量、周轉率,還預留了「分析」欄位。
第二站:n8n Workflow 1(資料接收)
n8n 的第一個 Workflow 叫 MeowClaw_Stock_Data_Receiver,做的事情很單純:
- 開一個 webhook endpoint 接收資料
- 資料萃取與格式化
- 寫入 PostgreSQL(我有個自己的小資料庫)
第三站:n8n Workflow 2(AI 分析)
第二個 Workflow 叫 Stock_Auto_Analysis,這是整個專案最「智慧」的部分:
- AI Agent 定時檢查資料庫有沒有新資料
- 如果有,就觸發分析流程
- 把分析結果渲染到網頁上(原本想做成自動寄信,但先求有再求好)
整條流程跑起來大概長這樣:
姊夫開網頁 → Bookmarklet 抓資料 → POST 到 GAS
↓
Google Sheet 儲存
↓
n8n 接收 → PostgreSQL
↓
AI Agent 定時分析 → 網頁呈現
技術上,完美。
轉折:跑通了,然後呢?
整套系統架起來之後,我興沖沖地跟姊夫說:「搞定了!你現在每天點一下書籤,資料就會自動跑進資料庫,然後 AI 會幫你分析周轉率前 100 的股票,找出值得注意的標的。」
姊夫:「喔⋯⋯可是我要的還得加上 K 線圖形態辨識耶。」
我:「⋯⋯蛤?」
姊夫:「像是這樣的線跟那樣的線交叉或者這根棒子跟那個棒子長相型態(請原諒我壓根聽不懂),我想讓系統自動抓出來。」
這時候我才發現,我做的是「周轉率排行 + 簡單的 AI 文字分析」,但他真正要的是「K 線圖形態辨識」,這是完全不同層級的需求。K 線圖形態需要:
- 抓取每支股票的歷史價格資料
- 圖像辨識或專業技術指標計算
- 懂技術分析的 domain knowledge
這不是 n8n 接個 Gemini API 就能搞定的,這可能得真的去串接金融資料 API、找指標演算法、動用 Claude Code 來搞。
換句話說:我把整條技術鏈都跑通了,但做的根本不是姊夫要的東西。
結局:成果廢棄,經驗留下
這個專案最後沒有正式上線。技術上它是成功的,Bookmarklet 能抓、GAS 能收、n8n 能分析,但需求上它是失敗的,因為我從頭到尾沒搞清楚姊夫真正要解決的問題。
回頭看,這是個經典的 over-engineering 案例:
- 我著迷於「怎麼繞過防爬蟲」
- 我享受「串起整條自動化流程」的成就感
- 我以為「技術跑通 = 專案成功」
但我忘了最重要的一件事:技術是用來解決問題的,不是用來炫技的。
如果當初我多花 10 分鐘問清楚「你具體要分析什麼」、「你的決策流程是怎樣」、「你看到這個資料之後會做什麼」,可能就不會走這條岔路了。
反思:為什麼「失敗的專案」反而教最多
這個專案雖然廢了,但留下的經驗值是實打實的:
- Bookmarklet 繞過 CSR 防爬:這招我之後又用在其他地方,屢試不爽
- n8n 的 webhook → PostgreSQL → AI Agent 流程:這個模式後來變成我處理「人工觸發 + 自動分析」任務的標準架構
- 需求對齊比技術炫技重要一百倍:這個教訓我記一輩子
而且說實在的,比起那些「一切順利」的專案,這種「技術成功但需求失敗」的案例,反而更有故事性、更值得寫進 Build Log 裡。
因為真正的成長,往往不是來自「做對了什麼」,而是來自「踩過什麼坑」。
最後我乾脆幫姊夫裝了 Antigravity,一個讓他自己去命令 AI 寫分析程式的工具。與其我隔著一層「不懂股市」的濾鏡去猜他要什麼,不如讓他自己跟 AI 對話,用他的語言描述他的需求。
我則繼續用我的 Bookmarklet + n8n 組合拳去自動化其他東西。各取所需,皆大歡喜,只是我的 PostgreSQL 裡多了一張永遠不會被查詢的 stock_turnover 表格,靜靜地躺在那裡提醒我:下次記得先問清楚需求。