把分散的超商商品心得整理成綜合評分、共識、聲量與可查證的購買依據。
Translation pending
CommercePython · TypeScriptGitHub @YuHsunWangOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Mobile-first convenience-store food review radar using NLP, Bayesian scoring, credibility weighting, and explainable product rankings.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
站在超商貨架前,30 秒決定買哪一個。 線上試用 · English 超商新品好不好吃,答案常常散在 PTT CVS 板的心得和推文裡。同一個商品可能有好幾篇討論,要一篇篇找其實很花時間。CVS Radar 把這些評價整理成排行榜,讓人可以快速比較大家怎麼評、口味怎麼樣,再決定要不要買。 站上收錄數千項超商商品,資料來自 2024 年 6 月以來累積的 PTT CVS 貼文與留言。這些內容會經過爬取、語意判讀和評分整理,並透過每日排程持續更新。 預設以「近期推薦」呈現最近值得買的商品,也能切換「討論熱度」看現在大家在聊什麼。 手機優先的卡片式排行;展開任一張卡,就能讀逐篇編號的作者評價摘要、正負向的評價重點,以及每一則的 PTT 原文連結。 超商新品的討論散在不同文章與推文裡。單看一篇心得會被作者口味帶著走,直接翻推文又得自己判斷反諷、離題回覆和樣本數夠不夠。CVS Radar 把這些訊號整理成商品層級、可以互相比較的資訊:Read the full README ↗
站在超商貨架前,30 秒決定買哪一個。
超商新品好不好吃,答案常常散在 PTT CVS 板的心得和推文裡。同一個商品可能有好幾篇討論,要一篇篇找其實很花時間。CVS Radar 把這些評價整理成排行榜,讓人可以快速比較大家怎麼評、口味怎麼樣,再決定要不要買。
站上收錄數千項超商商品,資料來自 2024 年 6 月以來累積的 PTT CVS 貼文與留言。這些內容會經過爬取、語意判讀和評分整理,並透過每日排程持續更新。
首頁支援手機快速篩選;網址會保留搜尋、品牌、分類、日期預設、排序、隱藏無分數商品的選項與最近展開的商品,複製網址即可分享目前的貨架。
商品詳情可展開官方營養標示:7-11 與全家提供整包裝熱量,部分全家商品另有每份營養素、份量與包裝份數;未匹配的資料保持空白。
超商新品的討論散在不同文章與推文裡。單看一篇心得會被作者口味帶著走,直接翻推文又得自己判斷反諷、離題回覆和樣本數夠不夠。CVS Radar 把這些訊號整理成商品層級、可以互相比較的資訊:
兩個頁面在建置時讀取同一份 web/public/data.json;所有語意判讀與統計都在發佈前的批次管線完成。載入後的搜尋與篩選都在本地即時完成,沒有後端。
| 層 | 技術 |
|---|---|
| 資料收集 | Python、Requests、Beautiful Soup |
| 語意標記 | LLM 判讀五層(情緒、商品名、心得摘句、代表留言、分類),各以內容指紋快取於 data/labels/*.csv |
| 評分 | 規則式情緒、SnowNLP adapter、貝氏收斂、可設定的時間衰減(目前關閉) |
| 服務層 | 框架無關的查詢 API,附供本地使用的 FastAPI adapter;公開網站不呼叫 API |
| 前端 | Next.js 15、React 19、TypeScript、Tailwind CSS、Lucide |
| 品質 | Pytest、Vitest、Ruff、TypeScript/Next.js production build |
| 部署 | Vercel(主要)+ GitHub Actions/GitHub Pages 靜態鏡像 |
一則留言不是一票。它先被判斷「算不算數」,再被賦予一個權重,最後和同一商品的其他意見一起收斂成分數。實作在 cvs_radar/scoring/compute.py,參數集中在 config.yaml。
weight = credibility(帳號) × role_weight(角色) × exp(−λ · 天數)
λ = 0.0,新舊意見不因時間而改變評分權重。前端「近期推薦」與「討論熱度」的 24 天半衰期排序仍保留,與商品分數分開計算。1.0:作者自評樂觀但只有一個樣本,留言群體較客觀但雜訊多,兩邊互相抵銷後不再另外加權。credibility = max(0.1, 1 − suspicion),下限 0.1 讓可疑帳號被壓低而不是被消失。cvs_radar/preference.py)對每個帳號統計它在各品牌下的留言數與平均情緒,再組出五個弱訊號;目前只對其中三項加權,品牌偏好兩項仍計算但權重為零。活躍度低於 5 則的帳號一律不評(樣本太少,算了也只是雜訊):
| 特徵 | 權重 | 抓什麼 |
|---|---|---|
one_sided | 0.00 | 捧一個品牌同時貶其他品牌(僅觀察) |
single_brand | 0.00 | 發言幾乎集中在單一品牌(僅觀察) |
extreme | 0.15 | 打分老是落在 |sentiment| ≥ 0.85 的兩端 |
burst | 0.45 | 24 小時內對同品牌連續留言 ≥ 3 則 |
template_like | 0.40 | 留言彼此的字元 bigram Jaccard ≥ 0.8(樣板文) |
這個分數只在內部降權,不對外標記任何帳號——弱訊號撐得起降權,撐不起點名。發佈的 JSON 不含帳號層欄位,由 cvs_radar/store.py 在寫入時清除並驗證,另由 tests/test_publish_privacy.py 守著。原始貼文 data/posts.jsonl 僅供本地管線使用,已忽略版控。
另有一條獨立的「業配指控」機制(SHILL_DETECTION):留言區指控某篇心得是業配時,降權的是被指控的那篇文章的作者,不是所有談論該商品的人。需要至少兩個不同帳號指控才算數——語料裡 16 篇被指控的文章有 12 篇只有一個人喊,喊「業配」的成本太低,一個人喊不能當結論。這條在 2026-08 修過一次:原本的寫法把整個商品的所有意見一起降權,而 μ、σ、n_eff 都會除以總權重,等比例縮放等於什麼都沒變,只有 fair01 被拉向 0.5 的先驗——結果一個評價差的商品,會因為被說是業配而分數變高。
per_user_cap 會先把同一帳號在同一商品下的所有發言(包含他既是作者又去別人串留言的情況)依各自意見權重加權平均,折成單一 stance,再進商品層。否則一個人在同串連噓五則就等於五個人。
fair01 = (prior_strength · μ₀ + Σ wᵢsᵢ) / (prior_strength + Σ wᵢ) # μ₀ = 0.5, prior_strength = 1.0
n_eff = (Σ wᵢ)² / Σ wᵢ²
n_eff 是有效樣本數:權重越集中在少數人身上,它越小。n_eff < 3 就判為「資料不足」,前端不顯示綜合評分也不顯示正/中/負百分比——約四分之一的商品因此留白。語料從 800 篇擴到 2,700 篇貼文之後,這個比例幾乎沒動,因為新進來的多半是討論度本來就低的舊商品。
此外,web/build_data.py 要求至少 4 位不同的合格留言者;不足時信心度降為「低」,推薦分與百分比分布也不顯示。作者意見會參與計分,但不能取代這 4 位留言者。
共識標籤由加權平均 μ 與加權標準差 σ 分帶:σ ≥ 0.26 為評價兩極;μ ≥ 0.68 且 σ ≤ 0.25 為一致好評;μ ≤ 0.45 且 σ ≤ 0.25 為一致負評;其餘為褒貶不一。這組門檻是 2026-07-20 對著實際分數分布重新校準的,不是拍腦袋值。
五個語意層(情緒、商品名、心得摘句、代表留言、分類)都由 LLM 判讀,但每筆結果以內容指紋當 key 快取進 data/labels/*.csv 並進版控。相同輸入不再呼叫模型,也必然產生相同的分數。
指紋的設計原則是把「模型當時看到的每一樣東西」都放進 key,五層各有各的組成。以情緒層為例(cvs_radar/sentiment.py 的 sentiment_fingerprint_v2):
SHA-256( 文章 ID | 推噓標籤 | NFKC 正規化留言 | 品牌 | 商品名 | 文章標題 | prompt 版本 )
品牌與商品名在裡面,是因為它們會改變答案——「好油」在鹹酥雞底下是稱讚,在咖啡底下是抱怨。第一版的 key 只有前三項,結果一篇文章被重新切分或改標題之後,仍然沿用在不同脈絡下判出的舊標籤。prompt 版本在裡面,則是為了讓改寫評分準則這件事會自動讓舊答案失效,而不是永遠有效。
因此驗證方式是改動前後各跑一次完整管線、比對整份結果集,而不是只看單元測試綠燈。例如導入指紋標籤那次的量化結果是:全目錄 93% 商品分數變動、581 項下降 145 項上升、平均 −10.1 分,抽驗掉最多的 5 項逐則核對後確認方向正確。
cvs_radar/ 解析、情緒、評分、服務與報表
config.yaml 評分、情緒與爬蟲參數
crawl_job.py PTT 爬取、儲存與重算入口
run.py 離線範例、快照查詢與管線 CLI
scripts/ 稽核、標記與摘句重建工具
ops/ 本地發佈管線與 cron wrapper
tests/ 後端與資料建置的回歸測試
web/
app/ Next.js App Router 入口
components/ 篩選、商品卡與詳情
lib/ 型別化的搜尋、篩選與排序邏輯
build_data.py 將結果與標記快取投影為公開資料
public/data.json 瀏覽器讀取的商品層級 payload
data/results.json 預先計算的商品層級快照
data/labels/ LLM 標記、人工覆寫與官方營養資料快取
docs/screenshots/ 由 production build 產生的截圖
.github/workflows/ CI 與 GitHub Pages 鏡像部署
需要 Python 3.12 以上;前端 CI 使用 Node.js 20。以下兩組步驟都從 repo 根目錄開始執行。
python -m pip install ".[core]"
cd web
npm ci
npm run build:data
npm test
npm run dev
開啟 http://localhost:3000。
python -m pip install -r requirements.txt
python run.py --demo
python -m uvicorn cvs_radar.api:app --reload
--demo 使用離線的合成範例貼文。實際爬取需要網路,並應遵守來源站台的速率限制與使用條款。API 是可選的本地服務,靜態網站不需要啟動它。
scripts/crawl_family_food.py 僅讀取全家食品安全專區的公開頁面,不共用限定
PTT URL 的 PttCrawler。使用前請再次查看該站的 robots.txt 與使用條款;尊重
網站負載(預設請求間隔 1 秒),不得繞過 CAPTCHA、登入或其他存取控制。照片預設
只保留官方 URL,只有明確傳入 --download-images 才會下載,且輸出目錄已忽略版控。
python scripts/crawl_family_food.py --start-page 1 --max-pages 3 \
--request-delay 1.5 --output data/family_food/products.jsonl
python scripts/crawl_family_food.py --max-pages 1 --download-images \
--image-dir data/family_food/images
上述命令會建立已忽略版控的輸出目錄。JSONL 每行是一項商品,包含 product_id、name、detail_url、image_urls、
serving_size、servings_per_container、八項營養欄位(calories、protein、
fat、saturated_fat、trans_fat、carbohydrates、sugar、sodium)、
ingredients、allergens 與 UTC crawled_at。數值欄位均為
{"value", "unit", "raw_text"};缺漏欄位為 null,無法解析的數值則保留 raw_text 並將 value 設為 null,不會誤填為零。
截至本功能開發時,執行環境的網路代理拒絕連到該主機,因此無法可靠宣稱現場頁面
是 server-rendered HTML 或 XHR/Fetch,也無法代為確認當下的 robots/條款內容。
實際執行前應在瀏覽器 Network 面板確認 /Web_FFD_2022/results/{page} 的分頁、
商品 ID、詳情 URL 與 JSON envelope;若網站 HTML 或 API 改版,請先保存不含個資的
新回應為本機 fixture,再同步更新 parse_list_html、parse_api_response 或
parse_product_html selector 與測試。測試不得直接連線官方網站。
從 repo 根目錄執行,與 CI 的檢查步驟一致:
python -m pip install ".[core,api,dev]"
python -m pytest -q
ruff check .
cd web
npm ci
npm run build:data
git diff --exit-code -- public/data.json
npm test
npx tsc --noEmit
npm run build
三百多個自動化測試涵蓋解析邊界情況、商品正規化、情緒歸屬、單人設限、共識分布、作者摘句抽取、分數校準、搜尋、分類與品牌篩選、日期邊界、四種排序模式與靜態匯出。
主要部署在 https://cvs-radar.vercel.app/,Vercel 以 web/ 為 Root Directory 連接本 repo,每次 push 到 main 就產生新的 production deployment。應用本身是 Next.js 靜態匯出(output: 'export')。
https://yuhsunwang.github.io/cvs-radar/ 是同一個站的靜態鏡像,由 .github/workflows/pages.yml 在每次 push 到 main 時重建 payload、以 /cvs-radar base path 建置、上傳並部署。
兩邊都讀同一份 web/public/data.json。這份資料由 repo 外部的排程主機執行本專案的 cron wrapper 後重算並 commit 回 main;repository 本身不包含或證明該主機的 crontab。排程若停擺,repo 不會自己更新,scripts/check_data_freshness.py 是可觀察的守門員。CI 則透過 .github/workflows/ci.yml 獨立跑前後端檢查。
本地發佈入口為 scripts/ops/rebackfill-cron.sh 與 scripts/ops/rebackfill.sh,後者依序爬取、標記、重算,再建置公開資料。scripts/fetch_official_kcal.py 與 scripts/fetch_official_nutrition.py 在建置前更新官方營養快取,失敗時沿用既有快取。五個標記層所需的 TypeSafe Jev 與本地 Codex CLI 設定見 Ops 管線。.github/workflows/refresh-data.yml 僅為手動復原備案,不執行這五個標記器,不能視為完整的語意更新。
這是獨立的作品集專案,與 PTT 及任何超商品牌均無關聯。商品評價來自公開的使用者內容,應視為決策輔助,而非客觀的商品品質主張。