當防爬蟲遇上書籤:一個技術成功但需求失敗的股票自動化專案

Build Log Feb 23, 2026

開場:家人的需求,工程師的條件反射

姊夫是股市老手,研究周轉率和 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 的邏輯超簡單,既然網站防的是「機器人」,那我就讓「真人」去開網頁,然後用一個書籤小工具從已經渲染好的頁面上把資料抓下來。

運作流程是這樣的:

  1. 人工操作:我(或姊夫)手動開啟目標網站,網頁正常載入、通過所有驗證
  2. 點擊書籤:觸發一段預先寫好的 JavaScript
  3. 抓取 DOM:從已渲染的 #rankingData 表格中提取資料(交易日期、股票代碼、名稱、周轉率、成交量、收盤價、漲跌幅)
  4. 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 分鐘問清楚「你具體要分析什麼」、「你的決策流程是怎樣」、「你看到這個資料之後會做什麼」,可能就不會走這條岔路了。

反思:為什麼「失敗的專案」反而教最多

這個專案雖然廢了,但留下的經驗值是實打實的:

  1. Bookmarklet 繞過 CSR 防爬:這招我之後又用在其他地方,屢試不爽
  2. n8n 的 webhook → PostgreSQL → AI Agent 流程:這個模式後來變成我處理「人工觸發 + 自動分析」任務的標準架構
  3. 需求對齊比技術炫技重要一百倍:這個教訓我記一輩子

而且說實在的,比起那些「一切順利」的專案,這種「技術成功但需求失敗」的案例,反而更有故事性、更值得寫進 Build Log 裡。

因為真正的成長,往往不是來自「做對了什麼」,而是來自「踩過什麼坑」。

最後我乾脆幫姊夫裝了 Antigravity,一個讓他自己去命令 AI 寫分析程式的工具。與其我隔著一層「不懂股市」的濾鏡去猜他要什麼,不如讓他自己跟 AI 對話,用他的語言描述他的需求。

我則繼續用我的 Bookmarklet + n8n 組合拳去自動化其他東西。各取所需,皆大歡喜,只是我的 PostgreSQL 裡多了一張永遠不會被查詢的 stock_turnover 表格,靜靜地躺在那裡提醒我:下次記得先問清楚需求。

Tags