今晚一個好去處,而不是 200 個選項
一張免費地圖,一眼看出今晚巴黎有哪些活動,並依文化與類別分類。目的是幫人輕鬆做出選擇,而不是被選項淹沒。
需求
忙了一整週,大家都想出門走走,但要滑過幾百個活動實在很累。選項太多,最後往往乾脆待在家。
構想
真正的困擾不是選項太少,而是太多。所以這個應用程式不會一打開就丟出一整份清單,而是從一個簡單的提問出發:今晚,在附近可以做什麼?
我打造了什麼
- 一個網站,一打開就在地圖上顯示今晚的活動,並以手機使用為優先設計。
- 自動收集程式,彙整巴黎市政府的開放資料、駐巴黎各國文化機構(FICEP)及其他公開來源的活動,各來源依自己的週期更新。
- 可依文化來源與活動類別瀏覽,隨當下心情挑選想去的地方。
- 安全機制:某個資料來源中斷時,網站會繼續提供最後一份正常的資料,狀態頁面則顯示「降級運作」,而不是整個當掉。
成果
- 27,623資料庫中的活動數(2026 年 10 月 6 日測量)
- 0在地圖上沒有位置的活動
- 4自動整合的開放資料與場館來源(2026 年 10 月 6 日測量)
自 2026 年 8 月 23 日起在 eventmap.hamdy-tabsissi.com 上線。資料庫中的每一個活動都標示在地圖上,資料也會自動更新。
畫面一覽
我學到了什麼
先驗證資料,再設計產品:快速調查資料來源後,發現 Nanterre 的活動是 0 筆、Montreuil 只有 14 筆,所以我把範圍縮小到巴黎,而不是去維護容易壞掉的爬蟲程式。另外,無聲無息的錯誤比直接當機代價更高:日期被當成文字來比較,一直悄悄把部分活動藏起來,直到測試時才被抓出來。
技術細節 技術堆疊、架構與決策——給技術背景的讀者
技術堆疊
- Python (FastAPI, asyncio)
- SQLite
- Leaflet + OpenStreetMap
- nginx
- systemd
- Ubuntu 22.04
架構一覽
- 訪客的手機或瀏覽器
- nginx — 反向代理、安全標頭
- FastAPI 應用程式(uvicorn) — 只綁定 127.0.0.1 的 systemd 服務,並設有 CPU 與記憶體配額
- SQLite — 活動、資料來源、去除重複
- 每 6 小時執行的收集程式 ← 巴黎市政府開放資料 + FICEP(OpenAgenda)
運作方式
- 以 asyncio 非同步匯入多個公開來源,包括 Que faire à Paris(巴黎市政府,ODbL 授權)、FICEP 與 OpenAgenda(.ics),去除重複後寫入 SQLite。現行來源清單可在 /health 查看。
- 依來源逐一進行具容錯能力的更新:失敗的來源絕不會清除既有資料;/health 會回報各來源的狀態,並切換為「degraded」。
- 稽核 13 個近郊網站後,發現沒有任何可用的資料來源(沒有 .ics,也沒有 JSON-LD Event),因此將範圍從 Nanterre–巴黎–Montreuil 縮小到巴黎。多來源架構仍保持就緒。
- 隔離的服務:不具 shell 的專用系統帳號、systemd 的 CPU 與記憶體配額,應用程式只綁定 127.0.0.1,並置於 nginx 之後。
- 測試中發現的程式錯誤:日期被當成字串比較,導致正式環境中的部分活動被無聲排除;已修正並寫成文件。
事件與修正
- 2026 年 9 月 24 日至 10 月 5 日,伺服器無法連上巴黎市政府的資料來源(DNS 錯誤):EventMap 持續提供最後一份正常的資料,/health 也標明故障來源並回報「degraded」。資料來源恢復後,一個資料庫約束(指向待刪除資料列的重複項目)讓每次更新都悄悄失敗,/health 卻誤報「ok」。我在本機重現問題、完成修正、以兩個測試涵蓋,並於當天重新部署:16,160 筆活動已更新。
仍待補強
- 僅限巴黎
- 取決於公開開放資料的品質