
一家代購店在噗浪,也就是台灣常用的微網誌上開團,每一團都要把日本官網的商品資料照固定格式手抄成一整串貼文。
這個內部工具讓人貼上商品網址就排好整串貼文,已上線給店裡少數幾個人共用。這篇隨進度更新。
貼文格式照舊貼文反推,排版交給模板
每一團的貼文結構都一樣,所以我把發成功的舊貼文讀回來當標準答案,排版做成固定模板,填不出來的欄位留下醒目的空格。
噗浪每則上限 360 字,但舊貼文有幾則超過還是發得出去,代表網址另有固定算法。拿這些貼文回推,每個網址最多算 38 字;官方沒寫這條,哪天被退回再往下修。
技術細節:模板欄位與字數常數
兩篇實際貼文透過 Plurk API 取
content_raw整理成docs/POST-FORMATS.md:主文放主圖與截止日,回文依序是注意事項、運費、商品樓;自訂表情為[花]這類方括號名稱,連結格式是URL (顯示文字),qualifier 為:。 模板定義在data/canned-texts.json的template.fields,罐頭文以{{欄位}}引用;auto自動帶入,required空白時輸出⟨欄位名⟩佔位符,when/whenEquals控制條件欄位。 商品樓自動排版,超過 10 張圖或 360 字就換樓,模仿實際貼文「單圖商品併樓」的做法。 字數常數PLURK_LINK_LEN = 38,受「純文字 50 字+8 個圖片網址」那樓限制:50 + 8 × 38 = 354 ≤ 360。 舊值 45 只算圖片網址、一般網址算原長,會把宣傳連結樓誤判超限。
會造成損失的錯在送出前擋下,其他只提醒
代購最怕收單截止打錯:寫成昨天,或晚於官方停止受理,客人下了單卻買不到。這類錯和沒填的空格在送出前一律擋下;匯率不在常見範圍、商品名還是日文,只跳提醒。
技術細節:發布前檢查
擋下:內文仍有
⟨…⟩、收單截止早於今天、收單截止晚於官方受注締切(取自商品詳細頁的orderDeadline)、單樓超過 360 字或 10 張圖。 提醒:距官方締切不到 2 天、匯率不在 0.15~0.35、通販境內運為 0、◎商品行含日文假名。
測試帳號只能發私人貼文,這條鎖在伺服器
工具同時接著測試帳號和正式帳號。測試帳號發的文一律只有自己看得到,這條寫在伺服器端,畫面上選了公開也照樣改掉。
噗浪的隱私欄位填 0 代表「所有朋友可見」,「只有我」要填自己的帳號編號。這個差別只有把發出去的文讀回來才看得到,所以驗收一律從平台讀回貼文的實際設定。
技術細節:本人限定的正確寫法
PLURK_ACCOUNT=test時applyPrivacyPolicy()忽略前端設定,固定送limited_to=[owner_id],/api/publish-all與/api/post-plurk都套用。owner_id由Users/me取得並依 token 快取;取不到就中止發布回 500,不退回[0]。 Plurk 的[0](API 讀回|0|)是所有朋友可見,網頁前端的「只有我」送的是[自己的 user_id];修正後讀回|owner_id|。 前端「本人限定」仍送[0],由伺服器轉換。
只存幾組金鑰,不值得多養一個資料庫
工具唯一要長期保存的,是登入噗浪用的幾組金鑰。為幾個字串多接一個資料庫,就多一個服務要顧,免費方案閒置太久還會被暫停。所以金鑰直接放進主機的環境設定,整套架在不綁卡的免費方案上,主機設在離日本商店近的東京。
技術細節:Vercel Hobby 部署
考慮過 Railway(需月費)、Vercel + Supabase(免費專案閒置 7 天暫停)、Firebase(Functions 要 Blaze 方案綁卡),最後採 Vercel Hobby,token 放環境變數。 Express 改
module.exports = app,直接執行才listen;api/index.js為入口,vercel.json設regions: ["hnd1"]、maxDuration: 300。 函式沒有可寫的檔案系統:OAuth request token 改放 10 分鐘 httpOnly cookie;/callback在正式站不寫檔,改顯示 token 請管理者貼進環境變數後重新部署。 密碼登入在lib/auth.js,HMAC 簽章 cookie,/api/*、/auth/*無 cookie 回 401;/api/proxy-image限白名單網域,避免變成開放代理。 東京節點實測:各站爬蟲本機與正式站結果一致,沒有一站擋海外 IP。
商店隨時改版,所以壞掉要能一鍵回報
工具靠爬蟲,也就是自動去商店網頁抄商品資料的小程式,每家店各一支,對方一改版就壞。所以擷取失敗時畫面會出現「回報」按鈕,按下就記進共用的 Google 試算表。
回報要有人按才記,因為網路一時不穩也會失敗,自動記錄只會灌出假警報。累積的回報能匯出成給 AI 讀的待辦清單,每筆附上對應的爬蟲,一次修完。
flowchart LR A[商店網站改版] --> B[擷取失敗] B --> C[有人按下回報] C --> D[記進共用試算表] D --> E[匯出待辦清單] E --> F[交給 AI 修對應的爬蟲]
技術細節:爬蟲註冊表與維護看板
每支爬蟲 export
{ name, homepage?, match(url), fetch(url), fetchDetail?(url) },scrapers/index.js依網址比對挑選;專用站必須排在通用的shopify.js之前。 回傳格式寫成契約記在CLAUDE.md:單品fetch()回扁平{ title, images:string[] },fetchDetail()的images為[{ key, url }];格式不合會在前端破圖或報data.images is not iterable。 看板存 Google Sheets:lib/sheets.js以服務帳號 JWT 呼叫 REST,簽章用 Node 內建crypto,不裝googleapis;寫入一律valueInputOption=RAW,防止=開頭的輸入被當成公式。 本機測試走同介面的lib/sheets-memory.js;看板 API 失敗回 502/503,不影響發文。 匯出為純文字,每筆附「比對:scrapers/xxx.js」,只命中通用 Shopify 時標「通用」,比對不到寫「無(網域)」。
發文已上線,訂單管理還沒動工
下一步是讓 AI 把日文商品名翻成中文,目前每件都手動改。排版仍交給模板,AI 產出的格式每次可能不一樣。
規劃中的後半段還沒開始:自動登記開團、讀留言整理訂單、自動回覆客人。正式帳號的專屬表情符號尚未驗證,最新一批爬蟲修正也還沒有正式站驗收紀錄。
技術細節:待辦
MVP-2(計畫中):
POST /api/translate批次翻譯,Gemini structured output 強制回傳[{ id, zh }];譯名品質靠系統提示、data/glossary.json詞彙表與翻譯快取三層,SDK 版本與模型名實作時再查證。 ROADMAP MVP-3~5:發文成功寫入 Sheets 開團紀錄、輪詢Responses/get由 AI 解析喊單、responseAdd自動回覆;看板的lib/sheets.js會直接沿用。 改善項:/api/publish-all目前一次跑完,改為前端逐步呼叫(上傳圖 → 主文 → 各回文),失敗可從斷點續發。 Hobby 排程每日只能跑 1 次,MVP-4 的留言輪詢要另找做法。
相關
- DealScout 運動折扣平台複盤 — 同樣靠爬蟲、要面對目標站改版