共發表了 46 篇文章。
目標: 學習 FastAPI 的基本概念,建立一個簡單的 API,並了解其核心特性,如自動文件。
預計時間: 約 2 小時 (包含實際操作編寫和運行程式的時間)
參考文件: FastAPI 官方文件 - First Steps (https://fastapi.tiangolo.com/tutorial/first-steps/)
教學大綱:
是什麼?為什麼用? (約 10 分鐘)
環境準備與安裝 (約 15 分鐘)
你的第一個 FastAPI 應用 (約 20 分鐘)
main.py
FastAPI
實例自動 API 文件 (約 15 分鐘)
2025-05-19GitHub Copilot 是一款革命性的 AI 程式設計副駕駛(AI Pair Programmer),基於大型語言模型(LLMs)開發,由 OpenAI 的 Codex 模型驅動,該模型在海量的開源程式碼資料上進行訓練。它能夠根據上下文理解您的編碼意圖,提供智能化的程式碼建議。
2025-05-192025 年 5 月 16 日,OpenAI 發布了 AI 程式設計代理工具「Codex」的研究預覽版本,旨在提升開發者的工作效率。
模型驅動:Codex 由 codex-1 模型驅動,該模型是基於 OpenAI 的 o3 推理模型,專為軟體工程任務進行微調。
功能特點:
運行環境:Codex 在受沙盒保護的雲端虛擬電腦中運行,與 GitHub 連接後,可預先載入儲存庫,支援自動化開發流程。
OpenAI 也更新了先前推出的開源編碼代理程式 Codex CLI,預設採用最新的 o4-mini 模型,透過 OpenAI API 提供服務,收費標準為每百萬個輸入詞元(token)1.5 美元、每百萬輸出詞元 6 美元。
2025-05-19本篇 Make.com 的核心工具教學!我們將一步步學習如何使用 Make.com 的三大核心模組:Router、Aggregator 和 Iterator,並結合實際案例,幫助您快速掌握自動化流程設計的技巧。
Router(路由器) 是 Make.com 的一個模組,用來將資料流程分成多條支線。每條路徑可以設定自己的篩選條件(Filter),以便針對不同的資料進行個別處理。
2025-04-21工具名稱 | 類別 | 技術特性 | 強項 | 適合用途 | 成本 / 可用性 |
---|---|---|---|---|---|
Kling 2.0 | 🎬 影片生成 | 高寫實 video diffusion 模型、支援複雜物理運動 | 超寫實影片生成、鏡頭邏輯佳 | 廣告片、科幻短片、虛擬拍攝 | 未公開;內測中 |
Seaweed AI(ByteDance) | 🎬 影片生成 | 輕量影片合成、照片轉動畫、人臉驅動 | 快速生成、娛樂導向、社群風格 | TikTok、社群影片、虛擬角色 | 未公開;預期整合 TikTok |
Canva Visual Suite 2.0 | 🎬 影片 + 設計工具 | AI 圖片/影片生成、排版轉換、Magic Switch | 無需專業背景即可製作內容 | 簡報、社群圖文、行銷素材 | 免費版 / Pro $14.99/月 |
Codex (新版) | 👨💻 寫程式助理 | 類 Devin agent,可讀 issue、自動修復與部署 | 自動開發流程、任務導向 | 開發者自動化、DevOps | 尚未開放;預計屬於 API 付費服務 |
GPT-4.1 / o4-mini | 👨💻+🧠 多模態 / 編程 / 助理 | 新一代 GPT-4 系列、速度更快、支援工具使用 | 編程、多模態推理、語意理解 | Chatbot、Code Interpreter、日常自動化 | ChatGPT Plus $20/月;API 分級計價 |
Claude Autonomous Research | 👨💻+🧠 AI 助理 / 長任務研究 | 多日任務記憶、自主研究規劃、自動摘要 | AI 助理 + 研究型用途 | 市場調查、法律摘要、研究助手 | 預計進入 Claude Pro / API 收費 |
Microsoft Copilot Vision | 🧠 AI 助理 / 圖像理解 | 多模態 Copilot,支援螢幕截圖理解 | Office 整合佳、圖像分析力強 | 報表分析、圖像摘要、PDF 理解 | M365 Copilot $30/月 |
Grok Studio + Memories | 🧠 個人 AI 助理 | 自創角色、自訂記憶、自我進化風格 | 個人化強、對話自然、可塑性高 | 聊天伴侶、任務提醒、定制 AI | X Premium+ $16/月 |
Gemini 2.5 Flash | 🧠 即時助理 | 超高速小模型、低延遲 | 適合行動裝置與即時應用 | Chatbot、智慧眼鏡、翻譯 | 預計 API 開放,成本低於 Gemini 1.5 Pro |
工具名稱 | 是否內建 | 可錄製操作 | 可編輯邏輯 | 支援 Web 操作 | 可呼叫 API |
---|---|---|---|---|---|
Automator | ✅ 是 | ⚠️ 部分支援 | ⚠️ 基本流程 | ❌ | ⚠️ 可搭配 Script |
AppleScript | ✅ 是 | ✅(可錄製 GUI 操作) | ✅ | ❌ | ⚠️ 透過 curl 可整合 |
Hammerspoon | ❌(需安裝) | ❌(純程式控制) | ✅ 可寫全邏輯 | ⚠️ 間接支援(需搭配 script) | ✅ 完整支援 API 請求 |
工具名稱 | 是否內建 | 可錄製操作 | 可編輯邏輯 | 支援 Web 操作 | 可呼叫 API |
---|---|---|---|---|---|
Power Automate Desktop | ✅ 是(Pro/Enterprise) | ✅ 支援完整錄製 | ✅ 條件邏輯強 | ✅ 支援 Web Automation | ✅ 內建 HTTP 請求模組 |
AutoHotKey (AHK) | ❌ 需安裝 | ❌ 無錄製器(可手寫操作模擬) | ✅ | ⚠️ 需搭配瀏覽器自動化套件 | ⚠️ 手動整合 API |
每日任務:
2025-04-19特點 | Gemini 2.5 Pro (Google) | ChatGPT API (GPT-4o - OpenAI) | Claude 3 Sonnet (Anthropic) | 分析與自動化意義 |
---|---|---|---|---|
核心模型能力 | 最新 Gemini 模型,增強推理、編碼與多模態理解能力。 | GPT-4 智能水準 + GPT-3.5 的速度,全面升級。 | 高可靠性、長文本理解佳,在穩定性與準確性上持續領先。 | 三者皆強大:GPT-4o 全能、Gemini 2.5 多模態最廣、Sonnet 在穩定與長文本方面表現突出。 |
上下文長度 (tokens) | 標準支援 1M tokens,企業版本支援至 2M。 | 128k tokens,速度快。 | 200k tokens,效能與長度兼顧。 | Gemini » Sonnet > GPT-4o。自動化任務中若處理大量單次資料輸入,Gemini 最具優勢。 |
多模態能力 | 原生支援圖像、音訊、影片;文字理解外也擅長視覺/影片分析。 | 支援文字、圖像、音訊;可輸出語音與生成圖像。 | 支援圖像與文字輸入;暫無音訊/影片處理能力。 | 若任務包含影片分析 → Gemini。需語音輸出/圖像生成 → GPT-4o。圖像理解任務三者皆可勝任。 |
速度與效能 | 整體提速,效能大幅優化,但處理超長輸入時仍可能略慢。 | 極快的反應速度,等同 GPT-3.5,維持 GPT-4 的智慧。 | 表現穩定,效能強但略慢於 GPT-4o。 | GPT-4o > Sonnet > Gemini(超長上下文場景除外)。對互動性強的自動化任務建議優先使用 GPT-4o。 |
成本結構與價格 | 約 $3.5/M input、$10.5/M output(標準);多模態另計。 | $5/M input、$15/M output;多模態另計。 | $3/M input、$15/M output;圖像處理計價另算。 | Sonnet 輸入成本最低、GPT-4o 綜合 CP 值最高。Gemini 若使用長上下文可節省 API 調用次數,總成本可能更低。 |
API 與整合 | Vertex AI、Google AI Studio,JSON Mode、Function Calling 支援穩定。 | OpenAI API + Azure 支援,開發資源最豐富,Function Calling 非常成熟。 | 支援 AWS、GCP 等多雲部署,Tool Use 逐步成熟中。 | OpenAI 生態最成熟。Google 整合 Vertex AI 最深。Anthropic 提供跨平台彈性更強。 |
Function Calling | 支援,逐步向 Agent 能力擴展。 | 非常成熟穩定,廣泛應用於工具調用與代理任務。 | Tool Use 概念推行中,API 逐步成熟。 | 自動化工具高度依賴 Function Calling 時,目前 OpenAI 優勢最大,但其他兩者正在追趕中。 |
JSON Mode / 結構化輸出 | 支援 JSON Mode。 | 支援 JSON Mode,最穩定。 | 支援 JSON Mode。 | 對 AI 工具輸出格式要求高者,三者皆可勝任。 |
安全性與可靠性 | 有安全審查與過濾。 | 改進中,具備較高穩定性與透明度。 | Constitutional AI 機制,強調模型可靠性與減害能力。 | 對企業自動化/客戶導向應用,Sonnet 的安全與可預測性設計最為理想。 |
社群與開發資源 | 成長中,依附 Google Cloud 生態系。 | 龐大的開發者社群與豐富資源支援。 | 快速擴張中,企業用戶導向,支援 AWS/GCP 等主流平台。 | OpenAI 生態完勝,適合快速起步與學習。其他兩者則在雲平台整合與企業導向工具上有優勢。 |
需求類型 | 推薦模型 |
---|---|
超大文件 (1M+ Tokens) 處理 | Gemini 2.5 Pro |
安全與可靠性優先的流程(如內部法務) | Claude 3 Sonnet |
快速交互式應用 (chatbot、workflow UI) | GPT-4o |
圖像生成與語音輸出 | GPT-4o |
音訊/影片輸入分析 | Gemini 2.5 Pro |
成本敏感且以長文本輸入為主 | Claude 3 Sonnet |
雲平台限制或偏好 GCP / AWS | Gemini / Sonnet |
高度工具整合/Function 調用需求 | GPT-4o(現階段最佳) |
根據以下三種角色需求,從開發者、設計美術、教學人員的視角,分析 Gemini 2.5 Pro / GPT-4o / Claude 3 Sonnet 的差異與優劣:
2025-04-18AI 繪圖工具速覽 (哪個適合你?)
工具名稱 (Tool) | 最大優點 (Biggest Pro) | 操作難度 (How Easy?) | 費用 (Cost) | 圖片感覺 (Image Vibe) | 最適合誰? (Best For Whom?) |
---|---|---|---|---|---|
ChatGPT (付費版) | 超方便,用聊天就能做圖,很懂你的要求 | ⭐⭐⭐⭐⭐ (非常容易) | 付費 (需訂閱 Plus) | 品質好、可加文字、風格多元 | ChatGPT 付費會員、想輕鬆出圖、需圖文並茂的人 |
Midjourney | 藝術感和細節最強,風格獨特電影感 | ⭐⭐ (需要學指令) | 付費 (不同等級方案) | 頂級藝術感、細節驚人、風格強烈 | 追求最高品質、藝術家、設計師 (願付費學習) |
Stable Diffusion (自己裝) | 彈性最大、完全免費 (模型)、可高度客製化 | ⭐ (非常複雜) | 免費 (但電腦要夠力) | 完全自訂,從照片到動漫,品質上限高 (看技術) | 技術高手、開發者、想完全控制、預算有限 (肯花時間) |
Stable Diffusion (線上平台) | 比自己裝簡單,功能多樣 | ⭐⭐⭐ (中等) | 有免費額度 & 付費方案 | 品質好、風格多樣 (依平台提供模型) | 想用 SD 強大功能又不想搞技術、需要特定模型的人 |
Adobe Firefly | 商業安全 (版權風險低),與 Adobe 軟體整合 | ⭐⭐⭐⭐ (容易) | 有免費額度 & Adobe 付費方案 | 品質穩定、商業風格、安全合規 | 設計師、企業、Adobe 用戶、超級重視版權的人 |
Leonardo.Ai | 功能豐富的 SD 平台,可自訓模型 | ⭐⭐⭐ (中等) | 有每日免費額度 & 付費方案 | 品質好、模型選擇多、可訓練特殊風格 | 遊戲開發、藝術家、想在網頁上玩 SD 進階功能的人 |
Microsoft Copilot | 免費使用 DALL-E 3,品質好又易用 | ⭐⭐⭐⭐⭐ (非常容易) | 免費 (每日有限制) | 品質好 (同 ChatGPT Plus)、理解力強 | 想免費體驗高品質 AI 繪圖的一般大眾、微軟用戶 |
Canva (Magic Media) | 無縫整合 Canva,設計流程中快速加圖 | ⭐⭐⭐⭐⭐ (非常容易) | 免費版次數少 & Canva Pro | 中上 (夠用),方便快速產出設計素材 | Canva 用戶、做簡報/社群貼文、需要快速方便配圖的人 |
簡單來說:
2025-04-18服務 | 定位 |
---|---|
Apify (apify.com) | 通用、可擴展的雲端平台,用於網頁抓取、資料提取、網頁自動化。提供基礎設施和開發工具。 |
Firecrawl (firecrawl.dev) | 專注於為 AI/LLM 應用(特別是 RAG)提供乾淨、結構化的網頁內容。作為一個易於使用的 API 服務。 |
Crawl AI (crawlai.org) | 開源 Python 框架/專案,目標是利用 AI/機器學習技術來優化和指導網路爬蟲的行為(例如,更智慧地決定抓取哪些連結)。 |
服務 | 能力等級 | 說明 |
---|---|---|
Apify | ⭐⭐⭐⭐⭐ (強) | 內建瀏覽器環境(Chromium),可執行 JS、處理 AJAX、應對大多數反爬機制(需配合良好代理策略)。可編寫程式碼處理登入、CAPTCHA(需整合第三方服務)。 |
Firecrawl | ⭐⭐⭐⭐ (中等偏強) | 可處理 JS 渲染網站。反封鎖能力內建,但對於極端複雜的登入流程或特定 CAPTCHA 可能無能為力(因為用戶無法直接干預過程)。 |
Crawl AI | ⭐⭐⭐ (取決於實現) | 需要自行整合 Playwright/Selenium 等工具來處理 JS 渲染。登入、CAPTCHA 等都需要自行編寫處理邏輯。 |
服務 | 能力等級 | 說明 |
---|---|---|
Apify | ⭐⭐⭐⭐⭐ (非常靈活) | 可提取任何網頁元素,輸出多種格式(JSON, CSV, Excel, HTML, RSS 等)。可自訂提取邏輯。 |
Firecrawl | ⭐⭐⭐⭐ (聚焦) | 主要輸出乾淨的 Markdown(移除廣告、導航等雜訊)或基於提供的 Schema 提取結構化數據。格式為 AI/LLM 優化。 |
Crawl AI | ⭐⭐⭐⭐⭐ (高度自訂) | 可使用 Python 的各種解析庫(BeautifulSoup, lxml 等)提取任何數據,格式完全由開發者定義。 |
服務 | 開發模式 | 說明 |
---|---|---|
Apify | 雲端託管 | 無需管理伺服器。可在 Apify Cloud 運行。提供線上 IDE 和本地開發工具。需要編寫 JS/Python 或配置 Actor。 |
Firecrawl | API 服務 | 無需開發或部署爬蟲本身,只需調用 API。 |
Crawl AI | 本地/私有部署 | 需要自行管理伺服器、依賴安裝、環境配置、部署流程。需要 Python 開發能力。 |
服務 | 特性 | 說明 |
---|---|---|
Apify | 內建且可選 | 提供強大的內建代理服務(付費),也可接入第三方代理。提供多種反封鎖技術。 |
Firecrawl | 內建且透明 | 代理和基本的反封鎖機制內建於服務中,用戶無需配置。 |
Crawl AI | 需自行解決 | 需要自行購買和管理代理伺服器,並自行實現反封鎖策略(如 User-Agent 輪換、延遲、指紋模擬等)。 |
服務 | UI | 說明 |
---|---|---|
Apify | 有 | 提供 Web UI 用於管理 Actors、排程、查看結果、監控運行。 |
Firecrawl | 有 (可能較簡單) | 可能提供儀表板用於管理 API 金鑰、查看用量和請求歷史。主要交互通過 API。 |
Crawl AI | 無 (預設) | 作為開源框架,預設沒有圖形化使用者介面。 |
服務 | 成本模式 | 說明 |
---|---|---|
Apify | 資源使用量計費 | 基於資源使用量(計算單位、代理流量、儲存)的點數付費。有免費方案。總體成本可能較高。 |
Firecrawl | API 請求計費 | 基於 API 請求次數或點數的付費。有免費方案。對於目標明確的任務可能成本效益更高。 |
Crawl AI | 基礎設施成本 | 軟體免費,但需要支付基礎設施(伺服器、帶寬、代理)和開發維護的人力成本。 |
服務 | 適合對象 |
---|---|
Apify | 適合需要功能全面、高度自訂、可擴展的通用爬蟲、數據提取和自動化任務,且願意投入學習成本或預算的團隊/個人。 |
Firecrawl | 適合需要快速、簡單地為 AI/RAG 應用準備大量網頁文本內容,不想處理複雜爬蟲細節和基礎設施的開發者。 |
Crawl AI | 適合技術能力強、希望完全控制爬蟲邏輯、探索 AI 驅動爬蟲技術、且願意自行承擔開發和基礎設施成本的開發者或研究團隊。 |
在人工智慧快速演進的時代,頂尖語言模型之間的較量不僅是技術指標的競爭,更關乎實際應用場景中的表現差異。本文將深入分析 Anthropic 的 Claude 3.7 Sonnet 與 OpenAI 的 ChatGPT 4.1(對應 GPT-4o)在程式開發與長篇小說創作兩大領域的表現。
Claude 3.7 Sonnet 是 Anthropic 最新推出的高階語言模型,承襲了 Claude 系列在推理能力、上下文處理與文字生成方面的優勢,同時大幅提升了運算速度。
ChatGPT 4.1(以 GPT-4o 為基礎)則代表了 OpenAI 目前最強大的多模態模型,以快速反應時間和全面的能力組合著稱。
核心優勢領域:
2025-04-17Cursor 是一款基於 Visual Studio Code (VS Code) 的 AI 程式碼編輯器,透過深度整合 AI 功能來增強開發體驗。它不僅是一個程式碼編輯器,更是一個開發助手,能夠理解程式碼、回答問題並協助撰寫和修改程式碼。
Cursor 的核心價值在於:
Hugo 與 WordPress 代表了兩種完全不同的網站建設方法。理解它們的根本差異是做出明智選擇的第一步。
graph TD subgraph "Hugo 架構" H1[內容: Markdown 文件] --> H2[Hugo 引擎] H2 --> H3[靜態 HTML 文件] H3 --> H4[訪問者] end subgraph "WordPress 架構" W1[後台管理介面] --> W2[PHP 程式] W2 <--> W3[(資料庫)] W2 --> W4[訪問者] end
Hugo 是一個靜態網站生成器,它預先將所有內容處理為靜態 HTML 文件。這意味著訪問者請求頁面時,伺服器只需提供已準備好的文件,無需即時處理或資料庫查詢。
2025-03-27用途 | XPath 語法 | 說明 | 範例 |
---|---|---|---|
選擇所有元素 | //tag |
選擇頁面上所有指定標籤的元素 | //div 選擇所有 div 元素 |
根據 ID 選擇 | //*[@id='ID值'] |
選擇具有特定 ID 的元素 | //*[@id='header'] 選擇 ID 為 header 的元素 |
根據 Class 選擇 | //*[contains(@class, 'CLASS值')] |
選擇具有特定 Class 的元素 | //*[contains(@class, 'btn')] 選擇 class 包含 btn 的元素 |
選擇精確 Class | //*[@class='CLASS值'] |
選擇 class 屬性完全匹配的元素 | //*[@class='btn-primary'] 選擇 class 等於 btn-primary 的元素 |
選擇屬性 | //*[@屬性='值'] |
選擇具有特定屬性值的元素 | //*[@href='https://example.com'] 選擇連結到 example.com 的元素 |
用途 | XPath 語法 | 說明 | 範例 |
---|---|---|---|
子元素 | //父元素/子元素 |
選擇直接子元素 | //div/p 選擇所有 div 的直接子 p 元素 |
所有後代 | //父元素//後代元素 |
選擇所有後代元素,不限層級 | //div//span 選擇所有 div 內的 span 元素,不論層級 |
父元素 | //元素/.. |
選擇元素的父元素 | //h1/.. 選擇所有 h1 元素的父元素 |
祖先元素 | //元素/ancestor::標籤 |
選擇元素的祖先元素 | //li/ancestor::div 選擇 li 元素的 div 祖先 |
上一個兄弟元素 | //元素/preceding-sibling::標籤[1] |
選擇緊鄰的前一個兄弟元素 | //li[3]/preceding-sibling::li[1] 選擇第三個 li 前的一個 li |
下一個兄弟元素 | //元素/following-sibling::標籤[1] |
選擇緊鄰的後一個兄弟元素 | //div/following-sibling::p[1] 選擇 div 後的第一個 p 元素 |
所有兄弟元素 | //元素/following-sibling::標籤 |
選擇所有後續兄弟元素 | //h2/following-sibling::p 選擇 h2 後的所有 p 元素 |
用途 | XPath 語法 | 說明 | 範例 |
---|---|---|---|
包含特定文本 | //*[contains(text(), '文本')] |
選擇包含指定文本的元素 | //*[contains(text(), '登入')] 選擇包含「登入」文本的元素 |
精確文本匹配 | //*[text()='文本'] |
選擇文本完全匹配的元素 | //*[text()='確認'] 選擇文本為「確認」的元素 |
屬性包含文本 | //*[contains(@屬性, '文本')] |
選擇屬性值包含特定文本的元素 | //*[contains(@href, 'product')] 選擇 href 包含 product 的元素 |
元素內容為空 | //*[not(node())] |
選擇沒有子節點的空元素 | //div[not(node())] 選擇所有空的 div 元素 |
用途 | XPath 語法 | 說明 | 範例 |
---|---|---|---|
第一個元素 | (//元素)[1] |
選擇符合條件的第一個元素 | (//tr)[1] 選擇頁面上第一個 tr 元素 |
最後一個元素 | (//元素)[last()] |
選擇符合條件的最後一個元素 | (//li)[last()] 選擇最後一個 li 元素 |
指定位置 | (//元素)[位置] |
選擇特定位置的元素 | (//td)[5] 選擇第五個 td 元素 |
位置範圍 | (//元素)[position() > N and position() < M] |
選擇位置在指定範圍內的元素 | (//tr)[position() > 1 and position() < 5] 選擇第 2 到第 4 個 tr |
奇數位置元素 | //元素[position() mod 2 = 1] |
選擇位置為奇數的元素 | //tr[position() mod 2 = 1] 選擇奇數行 |
偶數位置元素 | //元素[position() mod 2 = 0] |
選擇位置為偶數的元素 | //tr[position() mod 2 = 0] 選擇偶數行 |
用途 | XPath 語法 | 說明 | 範例 |
---|---|---|---|
AND 條件 | //元素[@屬性1='值1' and @屬性2='值2'] |
同時符合多個條件 | //input[@type='text' and @required] 選擇必填的文本輸入框 |
OR 條件 | //元素[@屬性1='值1' or @屬性2='值2'] |
符合任一條件 | //button[contains(@class, 'submit') or contains(@class, 'save')] 選擇提交或保存按鈕 |
NOT 條件 | //元素[not(@屬性='值')] |
不符合特定條件 | //div[not(contains(@class, 'hidden'))] 選擇不包含 hidden 類的 div |
多重條件 | //元素[條件1][條件2] |
依序篩選多個條件 | //div[contains(@class, 'product')][.//span[contains(@class, 'price')]] 選擇包含價格的產品 div |
用途 | XPath 語法 | 說明 |
---|---|---|
爬取表格數據 | //table[@id='data']//tr |
選擇特定表格的所有行 |
爬取產品列表 | //div[contains(@class, 'product-item')] |
選擇所有產品項目 |
爬取分頁連結 | //div[contains(@class, 'pagination')]//a |
選擇分頁區域的所有連結 |
爬取導航菜單 | //nav//li/a |
選擇導航菜單中的所有連結 |
爬取特定區塊 | //div[@id='content']//p |
選擇內容區域中的所有段落 |
爬取圖片連結 | //div[contains(@class, 'gallery')]//img/@src |
選擇圖庫中所有圖片的 src 屬性 |
爬取下拉選單選項 | //select[@id='options']/option |
選擇特定下拉選單的所有選項 |
用途 | XPath 語法 | 說明 | 範例 |
---|---|---|---|
獲取屬性值 | //元素/@屬性 |
直接獲取元素的特定屬性值 | //img/@src 獲取所有圖片的 src 屬性值 |
元素計數 | count(//元素) |
計算符合條件的元素數量 | count(//a[contains(@href, 'product')]) 計算產品連結數量 |
連接多個結果 | //元素1 | //元素2 |
合併多個 XPath 查詢結果 | //h1 | //h2 選擇所有 h1 和 h2 元素 |
選擇可見元素 | //*[not(contains(@style, 'display:none'))] |
選擇頁面上可見的元素 | //div[not(contains(@style, 'display:none'))] 選擇可見的 div |
獲取節點文本 | normalize-space(//元素) |
獲取元素的文本並去除多餘空白 | normalize-space(//h1) 獲取 h1 標題的文本 |
用途 | 技巧 | 說明 |
---|---|---|
等待元素加載 | 使用 WebDriverWait | 設置顯式等待,直到特定 XPath 選擇器能夠找到元素 |
處理 iframe | 先切換到 iframe 再定位 | driver.switch_to.frame(driver.find_element_by_xpath("//iframe[@id='content']")) |
處理 AJAX 加載 | 使用顯式等待 | 等待特定元素可見或可點擊,再進行後續操作 |
處理動態 ID | 使用 contains 或 starts-with | 對於動態生成的 ID,使用部分匹配而非精確匹配 |
處理浮動元素 | 使用相對位置定位 | 透過周圍的穩定元素來定位浮動元素 |
正則表達式是一種強大的文本模式匹配工具,以下是基本語法元素的對照表:
字元 | 說明 | 實例 | 匹配結果 |
---|---|---|---|
. |
匹配任意單個字元(除換行符外) | a.c |
abc, adc, a$c… |
\d |
匹配任意數字 | \d{3} |
123, 456… |
\w |
匹配字母、數字或下劃線 | \w+ |
abc, a123, test_123… |
\s |
匹配空白字元(空格、製表符、換行符) | a\sb |
“a b”, “a\tb”… |
^ |
匹配字串開頭 | ^Hello |
“Hello world” |
$ |
匹配字串結尾 | world$ |
“Hello world” |
* |
匹配前面的模式零次或多次 | a*b |
b, ab, aab… |
+ |
匹配前面的模式一次或多次 | a+b |
ab, aab… |
? |
匹配前面的模式零次或一次 | colou?r |
color, colour |
{n} |
匹配前面的模式恰好n次 | a{3} |
aaa |
{n,} |
匹配前面的模式至少n次 | a{2,} |
aa, aaa, aaaa… |
{n,m} |
匹配前面的模式n到m次 | a{2,4} |
aa, aaa, aaaa |
` | ` | 邏輯或 | `cat |
[] |
字元集合,匹配括號內任意字元 | [abc] |
a, b, c |
[^] |
字元集合,匹配非括號內的任意字元 | [^abc] |
d, e, f… |
() |
捕獲組,可以提取匹配的內容 | (abc) |
提取 “abc” |
(?:) |
非捕獲組,不提取匹配的內容 | (?:abc) |
不提取 “abc” |
修飾符 | 說明 |
---|---|
g |
全局匹配 (找出所有匹配項而非僅第一個) |
i |
忽略大小寫 |
m |
多行匹配 |
s |
使 . 可以匹配換行符 |
.*?
而不是 .*
(貪婪匹配)[^>]*
可以匹配標籤內任意屬性()
用於提取需要的內容以下是 20 個實用的正則表達式範例:
2025-03-24Notion 是一款多功能的工作空間工具,將筆記、資料庫、任務管理、文件協作等功能整合在一起。它可以用來管理個人事務、團隊專案、知識庫等多種用途。Notion 的特點是高度自定義和靈活性,使用者可以根據自己的需求打造專屬的工作環境。
mindmap root((Notion核心精神)) 簡化(Simplicity) 極簡的使用者介面 區塊化的內容設計 多種工具整合為一體 破格(Innovation) 打破傳統文件結構限制 多視圖呈現同一資料 模糊不同工具之間的界限 靈活(Flexibility) 無限嵌套的內容組織 完全自定義的工作流程 跨平台無縫適應
圖1.2:Notion三大核心精神
2025-03-23人工智能時代,我們不只是AI的使用者,更是與AI共同創造的夥伴。提詞(Prompting)是我們與AI溝通的橋樑,是解鎖AI潛力的鑰匙。掌握提詞技巧,就像學習一門新的語言,能夠讓我們更有效地利用AI工具,提升工作效率,釋放創意潛能。
本書將系統性地介紹AI提詞的各種技巧,從基礎的零樣本提詞到進階的思維鏈提詞和元提詞,從實用的CRAFT框架到精準的角色設定。無論你是AI新手還是資深用戶,都能在本書中找到提升AI使用體驗的關鍵方法。
在數位轉型的浪潮中,提詞技巧不僅是個人技能,更是組織競爭力的重要組成部分。讓我們一起探索AI提詞的奧秘,解鎖無限可能。
為什麼我們需要資料視覺化?
2025-03-20什麼是 Chart.js? Chart.js 是一個簡單而靈活的 JavaScript 圖表庫,基於 HTML5 Canvas 元素,使網頁開發者能夠輕鬆在網頁中創建各種互動式圖表。
為什麼選擇 Chart.js?
2025-03-18amCharts 是一套功能強大的 JavaScript 圖表庫,專為建立互動式、美觀的數據視覺化而設計。它提供了廣泛的圖表類型,從基本的柱狀圖和折線圖到複雜的地圖和儀表板。
amCharts 資料科學視覺化指南
2025-03-17Markdown 是一種輕量級標記語言,由 John Gruber 於 2004 年創建。它使用純文本格式,但可以轉換為結構化的 HTML 文件,非常適合撰寫文檔、筆記和網頁內容。
# 一級標題
## 二級標題
### 三級標題
#### 四級標題
##### 五級標題
###### 六級標題
*斜體文本* 或 _斜體文本_
**粗體文本** 或 __粗體文本__
***粗斜體文本*** 或 ___粗斜體文本___
~~刪除線文本~~
無序列表:
2025-03-17好的,以下是您提供的 Matplotlib 視覺化指南內容,以 Markdown 格式重新輸出:
Matplotlib 是 Python 中最常用的 2D 繪圖工具,幾個核心概念包括:
import matplotlib.pyplot as plt
import numpy as np
import json
# JSON 資料範例:
# 從 JSON 字串載入資料
json_data = '''
{
"x_values": [0, 1, 2, 3, 4, 5],
"y_values": [0, 1, 4, 9, 16, 25]
}
'''
data = json.loads(json_data)
# 基本繪圖流程
plt.figure()
plt.plot(data["x_values"], data["y_values"])
plt.xlabel("X 軸")
plt.ylabel("Y 軸")
plt.title("從 JSON 資料繪製的基本圖表")
plt.show()
Matplotlib 提供兩種繪圖 API:
2025-03-17Mermaid 是一個輕量級的圖表生成工具,讓你可以使用簡單的文本語法來創建各式各樣的視覺化圖表。本指南將從基礎概念開始,逐步深入到進階應用,包括如何使用 Python 和 ChatGPT 來輔助生成 Mermaid 圖表。
Mermaid 是一個 JavaScript 函式庫,允許使用者通過簡單的文本描述來生成圖表,類似於 PlantUML。這種「以程式碼繪製圖表」的方式非常適合在多種文本環境(如 Markdown 文件)中嵌入視覺化內容。
2025-03-17Plotly 是一個功能強大的 Python 資料視覺化函式庫,支援互動式與動態圖表,尤其適合用於網頁應用和資料分析。相較於 Matplotlib,Plotly 提供更豐富的互動性和美觀的預設樣式,能夠輕鬆製作出專業級的資料視覺化圖表。
本教學將從基礎到進階,示範如何使用 Plotly 繪製各種類型的圖表,並特別聚焦於如何從 JSON 資料中建立視覺化。
首先,我們需要安裝並匯入必要的套件:
2025-03-17資料視覺化是將複雜資料轉換成視覺圖形的過程,能夠幫助我們更有效地理解數據、發現趨勢與模式,並優化溝通效果。本課程將從基礎知識開始,逐步深入各種視覺化工具與技術,幫助學生掌握將資料轉化為有效視覺呈現的能力。
NumPy(Numerical Python 的簡稱)是 Python 中用於科學計算的核心函式庫。它提供了一個高性能的多維陣列對象 ndarray
,以及用於處理這些陣列的工具。
本課程旨在幫助學員從零開始,掌握 Pandas 的基本操作與資料清理技巧,能夠運用 Pandas 進行資料分析與處理。
import pandas as pd
import numpy as np # 通常也會一起匯入 NumPy
# 創建一個簡單的Series
data = [1, 2, 3, 4, 5]
series = pd.Series(data, name='Example Series')
print(series)
# 創建一個簡單的DataFrame
data = {'Name': ['Alice', 'Bob', 'Charlie'],
'Age': [25, 30, 35],
'Score': [88, 92, 85]}
df = pd.DataFrame(data)
print(df)
# 選擇欄位
print(df['Name'])
# 選擇列
print(df.iloc[0]) #選擇第一列
# 條件篩選
filtered_df = df[df['Age'] > 30]
print(filtered_df)
pd.read_csv()
df.to_csv()
# 讀取 CSV 檔案
# df = pd.read_csv('data.csv') # 假設有名為 data.csv 的檔案
# print(df)
# 儲存 DataFrame 為 CSV 檔案
# df.to_csv('output.csv', index=False) # 不儲存索引
isna()
、 isnull()
fillna()
dropna()
duplicated()
drop_duplicates()
astype()
# 創建一個包含NaN的DataFrame
df_nan = pd.DataFrame({'A': [1, 2, np.nan], 'B': [4, np.nan, 6]})
print(df_nan.isna()) # 判斷哪裡是NaN
print(df_nan.fillna(0)) # 將NaN替換為0
# 剔除缺失值
df_dropped = df_nan.dropna()
print(df_dropped)
# 判斷重複值
df = pd.DataFrame({'A': [1, 2, 2, 3], 'B': [4, 5, 5, 6]})
print(df.duplicated()) # 判斷重複值
# 刪除重複值
df_dropped = df.drop_duplicates() # 刪除重複值
print(df_dropped)
concat
和 merge
# 範例資料集
data = {'類別': ['A', 'A', 'B', 'B', 'C', 'C'],
'數量': [10, 15, 10, 10, 20, 25]}
df = pd.DataFrame(data)
# 依據 '類別' 分組並計算每組的總和
grouped_df = df.groupby('類別').sum()
print(grouped_df)
# 新增一個欄位
df['數量_兩倍'] = df['數量'] * 2
print(df)
# 改變欄位名稱
df.rename(columns={'數量': '產品數量'}, inplace=True)
print(df)
# 合併兩個DataFrame
df1 = pd.DataFrame({'Key': ['A', 'B', 'C'], 'Value': [1, 2, 3]})
df2 = pd.DataFrame({'Key': ['A', 'B', 'D'], 'Value': [4, 5, 6]})
df_merged = pd.merge(df1, df2, on='Key', how='inner')
print("\nMerge 合併後的 DataFrame:\n", df_merged)
# 範例資料集
data = {'A': [1, 2, 3, 4, 5], 'B': [6, 7, 8, 9, 10]}
df = pd.DataFrame(data)
# 計算各列的平均值
mean_values = df.mean()
print(mean_values)
# 計算每列的總和
sum_values = df.sum()
print(sum_values)
# 根據列A排序
sorted_df = df.sort_values(by='A', ascending=False)
print(sorted_df)
# 描述性統計
print(df.describe())
# 相關性分析
print(df.corr())
# 範例資料集
data = {'Salary': [50000, 60000, 70000], 'Score': [3.5, 4.2, 5.0]}
df = pd.DataFrame(data)
# 最小-最大正規化(Min-Max Scaling)
df['Salary'] = (df['Salary'] - df['Salary'].min()) / (df['Salary'].max() - df['Salary'].min())
df['Score'] = (df['Score'] - df['Score'].min()) / (df['Score'].max() - df['Score'].min())
print(df)
pd.date_range()
resample()
rolling()
# 時間序列的建立
dates = pd.date_range('2025-01-01', periods=6, freq='D')
df = pd.DataFrame({'Date': dates, 'Sales': [200, 220, 250, 270, 300, 350]})
df.set_index('Date', inplace=True)
print(df)
# 移動平均
df['Moving Average'] = df['Sales'].rolling(window=3).mean()
print(df)
import matplotlib.pyplot as plt
# 繪製線圖
df = pd.DataFrame({'A': [1, 2, 3, 4, 5], 'B': [6, 7, 2, 9, 10]})
df.plot(x='A', y='B', kind='line')
plt.show()
# 繪製直方圖
df['A'].plot(kind='hist')
plt.show()
# 範例
string_series = pd.Series(['apple', 'banana', 'cherry'])
upper_case = string_series.str.upper()
print("\n將字串轉為大寫:\n", upper_case)
eval()
和 query()
# 範例
df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
df['C'] = pd.eval('df["A"] + df["B"]')
print(df)
df_filtered = df.query('A > 1')
print(df_filtered)
# 範例
df = pd.DataFrame({
'A': [1, 2, np.nan, 4, 5],
'B': [5, np.nan, np.nan, 8, 9]
})
# 使用 fillna 填補遺漏值
df.fillna(value={'A': 0, 'B': 0}, inplace=True)
print(df)
# 移除或更正異常值
df = pd.DataFrame({
'A': [10, 20, 30, 1000, 50],
'B': [5, 5, 5, 5000, 5]
})
df[df > 100] = np.nan
print(df)
MinMaxScaler
from sklearn.preprocessing import MinMaxScaler
# 範例
data = {'Salary': [50000, 60000, 70000], 'Score': [3.5, 4.2, 5.0]}
df = pd.DataFrame(data)
scaler = MinMaxScaler()
df[['Salary', 'Score']] = scaler.fit_transform(df[['Salary', 'Score']])
print(df)
提供了一個 DateConverter 類別,用於處理各種日期格式的轉換,包括:
這個工具類別可以應用於需要處理不同日期格式的應用程式中,例如:歷史資料分析、多國語言日曆系統等。
DateConverter 類別包含多個靜態方法,每個方法負責一種日期轉換。
import datetime
from lunardate import LunarDate
class DateConverter:
"""
日期轉換工具類別,提供多種日期格式的轉換方法。
"""
# 民國年轉換為西元年
@staticmethod
def roc_to_ad(year):
"""將民國年轉換為西元年"""
if year <= 0:
raise ValueError("無效的民國年份: 年份必須大於0")
return year + 1911
# 西元年轉換為民國年
@staticmethod
def year_to_roc(year):
"""將西元年轉換為民國年"""
if year < 1912:
raise ValueError("年份不在有效範圍內: 民國年從1912年起始")
roc_year = year - 1911
return f"{roc_year}年"
# 支援的日期格式
_DATE_FORMATS = ['%Y-%m-%d', '%Y/%m/%d', '%Y年%m月%d日'] # 可擴展支持更多格式
# 日期字符串轉換為整數型別的年、月、日
@staticmethod
def convert_to_int_date(date_str):
"""將日期字符串轉換為整數型別的年、月、日"""
date = None
for date_format in DateConverter._DATE_FORMATS:
try:
date = datetime.datetime.strptime(date_str, date_format)
break
except ValueError:
pass
if date is None:
raise ValueError(f"無法解析日期:{date_str},請檢查日期格式是否正確。")
return date.year, date.month, date.day
# 農曆年轉天干地支年
@staticmethod
def lunar_year_to_gan_zhi(year):
"""將農曆年轉換為天干地支年"""
tian_gan = "甲乙丙丁戊己庚辛壬癸"
di_zhi = "子丑寅卯辰巳午未申酉戌亥"
gan_idx = (year - 4) % 10 # 天干循環
zhi_idx = (year - 4) % 12 # 地支循環
return tian_gan[gan_idx] + di_zhi[zhi_idx]
# 天干地支年轉換為多個西元年份
@staticmethod
def gan_zhi_to_lunar_years(gan_zhi_year):
"""將天干地支年轉換為多個西元年份"""
tian_gan = "甲乙丙丁戊己庚辛壬癸"
di_zhi = "子丑寅卯辰巳午未申酉戌亥"
gan = gan_zhi_year[0]
zhi = gan_zhi_year[1]
gan_idx = tian_gan.index(gan)
zhi_idx = di_zhi.index(zhi)
current_year = datetime.datetime.now().year
year_list = []
for i in range(current_year, current_year - 241, -1): # 遍歷過去240年
gan_idx_shift = (i - 4) % 10 # 根據天干的周期,向前位移4年
zhi_idx_shift = (i - 4) % 12 # 根據地支的周期,向前位移4年
if gan_idx_shift == gan_idx and zhi_idx_shift == zhi_idx:
year_list.append(i)
return year_list
# 國曆日期轉換為農曆日期
@staticmethod
def convert_to_lunar_date(year, month, day):
"""將國曆日期轉換為農曆日期"""
lunar_date = LunarDate.fromSolarDate(year, month, day)
return lunar_date
# 農曆日期轉換為國曆日期
@staticmethod
def convert_to_solar_date(year, month, day):
"""將農曆日期轉換為國曆日期"""
lunar_date = LunarDate(year, month, day)
return lunar_date.toSolarDate()
datetime
模組:用於處理日期和時間相關的操作。lunardate
模組:用於處理農曆日期。roc_to_ad(year)
:將民國年轉換為西元年。
year_to_roc(year)
:將西元年轉換為民國年。
@staticmethod
def roc_to_ad(year):
"""將民國年轉換為西元年"""
if year <= 0:
raise ValueError("無效的民國年份: 年份必須大於0")
return year + 1911
@staticmethod
def year_to_roc(year):
"""將西元年轉換為民國年"""
if year < 1912:
raise ValueError("年份不在有效範圍內: 民國年從1912年起始")
roc_year = year - 1911
return f"{roc_year}年"
convert_to_int_date(date_str)
:將日期字串轉換為整數型別的年、月、日。
_DATE_FORMATS
列表中定義的日期格式嘗試解析日期字串。 _DATE_FORMATS = ['%Y-%m-%d', '%Y/%m/%d', '%Y年%m月%d日'] # 可擴展支持更多格式
@staticmethod
def convert_to_int_date(date_str):
"""將日期字符串轉換為整數型別的年、月、日"""
date = None
for date_format in DateConverter._DATE_FORMATS:
try:
date = datetime.datetime.strptime(date_str, date_format)
break
except ValueError:
pass
if date is None:
raise ValueError(f"無法解析日期:{date_str},請檢查日期格式是否正確。")
return date.year, date.month, date.day
lunar_year_to_gan_zhi(year)
:將農曆年轉換為天干地支年。
gan_zhi_to_lunar_years(gan_zhi_year)
:將天干地支年轉換為可能的西元年份列表。
@staticmethod
def lunar_year_to_gan_zhi(year):
"""將農曆年轉換為天干地支年"""
tian_gan = "甲乙丙丁戊己庚辛壬癸"
di_zhi = "子丑寅卯辰巳午未申酉戌亥"
gan_idx = (year - 4) % 10 # 天干循環
zhi_idx = (year - 4) % 12 # 地支循環
return tian_gan[gan_idx] + di_zhi[zhi_idx]
@staticmethod
def gan_zhi_to_lunar_years(gan_zhi_year):
"""將天干地支年轉換為多個西元年份"""
tian_gan = "甲乙丙丁戊己庚辛壬癸"
di_zhi = "子丑寅卯辰巳午未申酉戌亥"
gan = gan_zhi_year[0]
zhi = gan_zhi_year[1]
gan_idx = tian_gan.index(gan)
zhi_idx = di_zhi.index(zhi)
current_year = datetime.datetime.now().year
year_list = []
for i in range(current_year, current_year - 241, -1): # 遍歷過去240年
gan_idx_shift = (i - 4) % 10 # 根據天干的周期,向前位移4年
zhi_idx_shift = (i - 4) % 12 # 根據地支的周期,向前位移4年
if gan_idx_shift == gan_idx and zhi_idx_shift == zhi_idx:
year_list.append(i)
return year_list
convert_to_lunar_date(year, month, day)
:將國曆日期轉換為農曆日期。
LunarDate.fromSolarDate()
方法將國曆日期轉換為 LunarDate
物件。convert_to_solar_date(year, month, day)
:將農曆日期轉換為國曆日期。
LunarDate()
建立 LunarDate
物件,然後使用 toSolarDate()
方法轉換為國曆日期。 @staticmethod
def convert_to_lunar_date(year, month, day):
"""將國曆日期轉換為農曆日期"""
lunar_date = LunarDate.fromSolarDate(year, month, day)
return lunar_date
@staticmethod
def convert_to_solar_date(year, month, day):
"""將農曆日期轉換為國曆日期"""
lunar_date = LunarDate(year, month, day)
return lunar_date.toSolarDate()
以下是如何使用 DateConverter
類別中的方法的範例:
sudo apt update && sudo apt upgrade -y
sudo apt install -y nvidia-driver-535
sudo reboot
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-ubuntu2204.pin
sudo mv cuda-ubuntu2204.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/12.2.2/local_installers/cuda_12.2.2_535.104.05_linux.run
sudo sh cuda_12.2.2_535.104.05_linux.run --silent --toolkit
export PATH=/usr/local/cuda-12.2/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda-12.2/lib64:$LD_LIBRARY_PATH
nvidia-smi
nvcc --version
sudo mkdir -p /mnt/ssd_swap
sudo fallocate -l 256G /mnt/ssd_swap/swapfile
sudo chmod 600 /mnt/ssd_swap/swapfile
sudo mkswap /mnt/ssd_swap/swapfile
sudo swapon /mnt/ssd_swap/swapfile
echo '/mnt/ssd_swap/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
sudo apt install -y python3-venv python3-pip
python3 -m venv ~/llm_env
source ~/llm_env/bin/activate
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
pip install deepseek-llm
pip install langchain faiss-cpu
from deepseek import DeepSeekModel
model = DeepSeekModel.from_pretrained("deepseek-ai/deepseek-llm-7b")
model.to("cuda") # 使用 RTX 3060
from transformers import BitsAndBytesConfig
quantization_config = BitsAndBytesConfig(load_in_4bit=True)
model = DeepSeekModel.from_pretrained("deepseek-ai/deepseek-llm-7b", quantization_config=quantization_config)
torch.compile()
提高推理速度(需要 PyTorch 2.0 以上) import torch
model = torch.compile(model)
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
vector_db = FAISS.load_local("/mnt/ssd_swap/faiss_db", OpenAIEmbeddings())
from langchain.document_loaders import TextLoader
loader = TextLoader("large_dataset.txt", chunk_size=512)
docs = loader.load()
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(model_name="deepseek-llm-7b", streaming=True)
✅ NVIDIA 驅動 ➝ 安裝 CUDA 12,確認 nvidia-smi
正常運行 ✅ 增加 SWAP ➝ 在 Micron X9 建立 256GB SWAP,並調低 swappiness=10
✅ 安裝 PyTorch + DeepSeek LLM ➝ 用 CUDA 12 運行 ✅ 降低 VRAM 需求 ➝ 用 4-bit 量化(BitsAndBytesConfig) ✅ 提高推理速度 ➝ 用 torch.compile()
編譯模型 ✅ 優化 LangChain + FAISS ➝ 使用分批處理、Streaming API
以下是針對 MacBook Pro 2019(含 T2 安全晶片)安裝 Ubuntu 24.04 的操作程序整理,我會將步驟簡化並整合為清晰的指南。因為 MacBook Pro 2019 有 T2 晶片,安裝 Ubuntu 需要特別調整安全設定並準備適當的啟動媒體。請注意,Ubuntu 24.04 是較新的版本,T2 支援可能需要額外的内核修補或驅動程式,以下步驟假設你希望從外部磁碟啟動或安裝到內部 SSD。
下載 Ubuntu 24.04 ISO
2025-02-25Ubuntu BreezyVoice 從零開始完整安裝設定建議 (基於官方文件)
Step 1: 環境準備 (系統更新與基本工具安裝)
開啟 Ubuntu 終端機 (Terminal):在 Ubuntu 桌面環境中,您可以透過應用程式選單搜尋 “Terminal” 或使用快捷鍵 Ctrl + Alt + T
開啟終端機。
更新系統套件列表與已安裝套件: 保持系統套件最新,有助於避免相容性問題。
sudo apt update
sudo apt upgrade -y
安裝必要的系統工具: 這些工具包含程式碼下載、編譯以及套件管理所需的基礎工具。
sudo apt install -y git curl wget apt-utils
Step 2: 安裝 Python 3.10 環境
BreezyVoice 需要 Python 3.10 版本。請確認您的 Ubuntu 環境中已安裝或安裝正確版本的 Python。
1. **檢查 Python 版本**: 先確認系統中預設的 Python 版本。
```bash
python3 --version
```
如果版本不是 3.10.x,您需要安裝 Python 3.10。
2. **安裝 Python 3.10 (如果需要)**: 您可以使用 `deadsnakes` PPA 來安裝 Python 3.10。
```bash
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update
sudo apt install -y python3.10 python3.10-venv # 同時安裝虛擬環境工具
```
3. **確認 Python 3.10 安裝成功**: 再次檢查版本,確認已安裝 Python 3.10。
```bash
python3.10 --version
```
4. **設定 Python 3.10 為預設 (可選)**: 如果您希望之後直接使用 `python3` 指令就指向 Python 3.10,可以考慮設定預設版本 (請謹慎操作,可能影響其他系統工具)。
```bash
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.10 1
python3 --version # 再次確認預設 python3 版本
```
Step 3: 下載 BreezyVoice 程式碼
2025-02-25requests
套件,從 CDC API 提取 Covid-19 與流感數據,並將其存為 CSV 格式。requests
、pandas
。requests
與 pandas
,並測試程式碼。pip install requests pandas
import requests, pandas
(無錯誤即成功)。9mfq-cb36.json
)、流感 (vh55-3he6.json
)。requests
與 pandas
。import requests
url = "https://data.cdc.gov/resource/9mfq-cb36.json"
response = requests.get(url)
print(f"API 狀態碼:{response.status_code}")
requests.get()
:發送 API 請求。pd.DataFrame()
:將 JSON 轉為表格。import requests
import pandas as pd
# CDC Covid-19 API
url = "https://data.cdc.gov/resource/9mfq-cb36.json"
# 發送請求
response = requests.get(url)
if response.status_code == 200:
data = response.json()
df = pd.DataFrame(data)
df = df[["submission_date", "state", "tot_cases", "tot_death"]]
df["submission_date"] = pd.to_datetime(df["submission_date"])
csv_filename = "covid19_cdc_data.csv"
df.to_csv(csv_filename, index=False)
print(f"已成功儲存 Covid-19 歷年數據至 {csv_filename}")
else:
print(f"❌ API 請求失敗,狀態碼:{response.status_code}")
covid19_cdc_data.csv
,檢查日期、州別與病例數。week
、ili_activity_level
)。import requests
import pandas as pd
# CDC Flu API
flu_url = "https://data.cdc.gov/resource/vh55-3he6.json"
# 發送請求
response = requests.get(flu_url)
if response.status_code == 200:
flu_data = response.json()
flu_df = pd.DataFrame(flu_data)
flu_df = flu_df[["week", "region", "ili_activity_level", "activity_level_label"]]
flu_csv_filename = "flu_cdc_data.csv"
flu_df.to_csv(flu_csv_filename, index=False)
print(f"已成功儲存流感數據至 {flu_csv_filename}")
else:
print(f"❌ API 請求失敗,狀態碼:{response.status_code}")
flu_cdc_data.csv
,找出流感活動等級最高的地區。covid19_cdc_data.csv
。flu_cdc_data.csv
。matplotlib
繪製 Covid-19 或流感趨勢圖。.py
格式)。pytrends
套件提取 LLM(大型語言模型)相關關鍵字的搜尋趨勢,並將資料輸出為 CSV 格式。pytrends
、pandas
。pytrends
並測試程式碼。pytrends
套件如何透過 API 提取資料。pytrends
:
pip install pytrends
import pytrends
(無錯誤即成功)。TrendReq
:初始化 Google Trends API。timeframe
(時間範圍)、geo
(地區)。pytrends
。from pytrends.request import TrendReq
pytrends = TrendReq(hl="en-US", tz=360)
print("成功連接到 Google Trends API!")
build_payload
:設定關鍵字與搜尋條件。interest_over_time()
:獲取時間序列數據。to_csv()
:輸出為 CSV。from pytrends.request import TrendReq
import pandas as pd
# 初始化 API
pytrends = TrendReq(hl="en-US", tz=360)
# 定義關鍵字
keywords = ["GPT-4", "Llama 3", "Gemini AI", "Claude 3", "Mistral AI"]
# 設定搜尋條件
pytrends.build_payload(kw_list=keywords, timeframe="now 30-d", geo="US")
# 獲取趨勢數據
trend_data = pytrends.interest_over_time()
trend_data = trend_data.drop(columns=["isPartial"])
# 輸出 CSV
csv_filename = "llm_trends.csv"
trend_data.to_csv(csv_filename)
print(f"已成功儲存 LLM 搜尋趨勢數據至 {csv_filename}")
print(trend_data.head())
geo="US"
為 "TW"
(台灣)或 ""
(全球),重新運行並比較結果。llm_trends.csv
,確認包含日期與關鍵字搜尋熱度。interest_by_region()
:
# 取得各國趨勢數據
region_trends = pytrends.interest_by_region()
# 輸出 CSV
csv_filename = "llm_trends_by_country.csv"
region_trends.to_csv(csv_filename)
print(f"已成功儲存各國 LLM 搜尋趨勢數據至 {csv_filename}")
print(region_trends.head())
llm_trends_by_country.csv
,找出搜尋熱度前 5 名的國家。interest_over_time()
適合分析時間趨勢。interest_by_region()
適合比較地區差異。llm_trends.csv
(時間趨勢)。llm_trends_by_country.csv
(地區趨勢)。keywords
,加入其他 AI 技術(如 “Quantum Computing”)。timeframe="2023-01-01 2023-12-31"
,分析 2023 全年趨勢。.py
格式)。requests
與 BeautifulSoup
爬取靜態內容,以及 Selenium
爬取動態內容,並將資料整理為 CSV 格式。requests
、beautifulsoup4
、pandas
、selenium
、webdriver-manager
。requests
爬取(40 分鐘)Selenium
爬取(50 分鐘)requests
(靜態)與 Selenium
(動態)的差異。requests
與 BeautifulSoup
爬取(40 分鐘) pip install requests beautifulsoup4 pandas
requests.get()
:發送 HTTP 請求。BeautifulSoup
:解析 HTML,提取標籤內容。pandas
:整理資料並輸出 CSV。import requests
from bs4 import BeautifulSoup
import pandas as pd
URL = "https://biggo.com.tw/s/?q=RTX+4090"
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
}
response = requests.get(URL, headers=HEADERS)
if response.status_code == 200:
soup = BeautifulSoup(response.text, "html.parser")
products = soup.find_all("div", class_="Product")
product_list = []
for product in products:
try:
title = product.find("h2").text.strip()
price = product.find("span", class_="price").text.strip()
seller = product.find("span", class_="seller").text.strip()
link = product.find("a", class_="title")["href"]
product_list.append([title, price, seller, f"https://biggo.com.tw{link}"])
except AttributeError:
continue
df = pd.DataFrame(product_list, columns=["商品名稱", "價格", "賣場", "連結"])
print(df)
df.to_csv("biggo_rtx4090_prices.csv", index=False, encoding="utf-8-sig")
print("✅ 已成功爬取並儲存 RTX 4090 價格資訊!")
else:
print("❌ 無法取得 BigGo 網頁資料,請檢查 URL 或 Headers 設定。")
biggo_rtx4090_prices.csv
,確認包含名稱、價格等欄位。Selenium
爬取動態內容(50 分鐘) pip install selenium pandas webdriver-manager
webdriver-manager
。webdriver.Chrome()
:模擬瀏覽器。--headless
:無視窗運行。find_elements()
:動態提取資料。import time
import pandas as pd
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument("--disable-gpu")
chrome_options.add_argument("--no-sandbox")
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)
URL = "https://biggo.com.tw/s/?q=RTX+4090"
driver.get(URL)
time.sleep(3)
products = driver.find_elements(By.CLASS_NAME, "Product")
product_list = []
for product in products:
try:
title = product.find_element(By.TAG_NAME, "h2").text.strip()
price = product.find_element(By.CLASS_NAME, "price").text.strip()
seller = product.find_element(By.CLASS_NAME, "seller").text.strip()
link = product.find_element(By.CLASS_NAME, "title").get_attribute("href")
product_list.append([title, price, seller, link])
except Exception as e:
print(f"❌ 抓取錯誤:{e}")
continue
driver.quit()
df = pd.DataFrame(product_list, columns=["商品名稱", "價格", "賣場", "連結"])
df.to_csv("biggo_rtx4090_prices_selenium.csv", index=False, encoding="utf-8-sig")
print("✅ 已成功爬取並儲存 RTX 4090 價格資訊!")
biggo_rtx4090_prices_selenium.csv
,確認資料完整性。Selenium
的優勢。requests
):適合靜態網頁,快速但受限於動態內容。Selenium
):適合動態網頁,功能強大但較慢。biggo_rtx4090_prices.csv
(方法 1)。biggo_rtx4090_prices_selenium.csv
(方法 2)。matplotlib
繪製價格趨勢圖。.py
格式)。Selenium
進行網頁爬取。lxml
處理 HTML 資料。WebDriver
的基本操作。本程式使用 Selenium
來控制瀏覽器,lxml
來解析 HTML。請執行以下指令安裝必要的套件:
yfinance
抓取農產品交易數據,建立 FastAPI 服務,並透過 Make.com 將數據自動存入 Notion。yfinance
、fastapi
、uvicorn
、pandas
、requests
。yfinance
抓取數據(30 分鐘)yfinance
抓取交易數據(30 分鐘) pip install yfinance pandas
yfinance.Ticker()
:獲取商品數據。history()
:提取 OHLC 與交易量。import yfinance as yf
import pandas as pd
commodities = {
"黃豆": "ZS=F",
"玉米": "ZC=F",
"小麥": "ZW=F"
}
def fetch_commodity_data():
result = {}
for name, symbol in commodities.items():
data = yf.Ticker(symbol).history(period="1d")
if not data.empty:
latest = data.iloc[-1]
result[name] = {
"開盤": latest["Open"],
"最高": latest["High"],
"最低": latest["Low"],
"收盤": latest["Close"],
"成交量": latest["Volume"]
}
return result
data = fetch_commodity_data()
print(data)
period="1d"
為 "5d"
,觀察多日數據變化。pip install fastapi uvicorn
@app.get()
:定義 API 端點。uvicorn.run()
:啟動服務。http://localhost:8000/commodities
。from fastapi import FastAPI
import uvicorn
from your_module import fetch_commodity_data # 假設前述函數在另一檔案
app = FastAPI()
@app.get("/commodities")
def get_commodities():
return fetch_commodity_data()
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
http://localhost:8000/commodities
,檢查回傳數據。http://localhost:8000/commodities
(或公開伺服器 URL)。開盤價
→ Open
)。yfinance
:快速獲取金融數據。FastAPI
:提供外部存取介面。Make.com
:實現自動化與資料整合。yfinance
程式,抓取多月數據並計算平均價格。/commodities/weekly
)。.py
格式)。yfinance
套件,高效獲取 NVIDIA 股票的歷史數據。matplotlib
和 pandas
進行專業數據可視化,解讀 NVIDIA 股價走勢。yfinance
(獲取股票數據)、matplotlib
(數據可視化)和 pandas
(數據處理)。yfinance
套件:pip install yfinance
matplotlib
和 pandas
已安裝,若未安裝,執行:pip install matplotlib pandas
import yfinance as yf
# 下載 NVIDIA(NVDA)股票數據
nvda = yf.Ticker("NVDA")
# 獲取歷年季度交易數據
df = nvda.history(period="max", interval="3mo")
# 顯示前 10 筆數據
print(df.head(10))
import yfinance as yf
:導入 yfinance
套件。yf.Ticker("NVDA")
:指定股票代號為 NVIDIA(NVDA)。.history(period="max", interval="3mo")
:獲取最大時間範圍的季度數據。print(df.head(10))
:顯示前 10 筆數據,確認數據獲取。 Open High Low Close Volume Dividends Stock Splits
Date
1999-01-01 0.424 0.449 0.364 0.403 66900000 0.0 0.0
1999-04-01 0.403 0.509 0.380 0.490 45800000 0.0 0.0
...
import yfinance as yf
import pandas as pd
# NVIDIA(NVDA)股票數據
nvda = yf.Ticker("NVDA")
# 獲取季度數據
nvda_df = nvda.history(period="max", interval="3mo")
# 顯示最近 5 筆季度數據
print("NVIDIA 最近 5 筆季度數據:")
print(nvda_df.tail(5))
# 計算季度平均收盤價
quarterly_avg = nvda_df["Close"].resample("Q").mean()
print("\nNVIDIA 季度平均收盤價:")
print(quarterly_avg.tail(5))
# 計算季度報酬率
quarterly_returns = nvda_df["Close"].resample("Q").last().pct_change()
print("\nNVIDIA 季度報酬率:")
print(quarterly_returns.tail(5))
# 計算移動平均線 (2季)
nvda_df['2季移動平均'] = nvda_df['Close'].rolling(window=2).mean()
#計算移動平均線 (4季)
nvda_df['4季移動平均'] = nvda_df['Close'].rolling(window=4).mean()
print(nvda_df.tail())
import matplotlib.pyplot as plt
# 繪製 NVIDIA 季度收盤價趨勢
plt.figure(figsize=(12, 6))
plt.plot(df.index, df["Close"], label="NVIDIA 收盤價")
plt.plot(df.index, df["2季移動平均"], label="2季移動平均線")
plt.plot(df.index, df["4季移動平均"], label="4季移動平均線")
plt.title("NVIDIA (NVDA) Quarterly Closing Prices with Moving Averages")
plt.xlabel("Year")
plt.ylabel("Stock Price (USD)")
plt.legend()
plt.grid(True)
plt.show()
#繪製季度報酬率
plt.figure(figsize=(12,6))
quarterly_returns = df["Close"].resample("Q").last().pct_change()
quarterly_returns.plot(kind='bar', color='skyblue')
plt.title("NVIDIA Quarterly Returns")
plt.xlabel("Quarter")
plt.ylabel("Returns")
plt.grid(axis='y')
plt.show()
plt.plot()
:繪製收盤價與移動平均線趨勢圖。plt.figure(figsize=(12, 6))
:設定圖表大小。plt.title
、plt.xlabel
、plt.ylabel
:添加圖表標題與軸標籤。plt.legend()
:加上圖示。plt.grid(True)
:加上網格線。quarterly_returns.plot(kind='bar', color='skyblue')
:繪製季度報酬率的柱狀圖。本課程將從零開始,帶領您學習如何運用 Python 的 pandas
、NumPy
、SciPy
和 re
等套件,清洗並整理網拍資料。您將在本課程中掌握資料清理的完整流程與實用技巧。
pip
安裝 pandas
、numpy
、scipy
、matplotlib
、seaborn
、scikit-learn
等套件。pip install pandas numpy scipy matplotlib seaborn scikit-learn
在 Python 程式碼中導入課程中將使用的套件。
2025-02-23本篇文章將根據 yfinance GitHub 連結介紹此專案,並示範如何使用它來擷取股票與期貨資料,並運用 Pandas 進行資料分析。
yfinance 是一個 Python 開源函式庫,其主要功能是從 Yahoo Finance! 網站上抓取金融市場數據。透過 yfinance,使用者可以輕鬆地取得股票、指數、貨幣、加密貨幣、ETF、基金以及期貨等金融商品的:
2025-02-23處理從中央氣象署開放資料平台取得的地震資料 JSON 檔案,並進行資料清理與統計分析。
API 參考連結:
範例 Curl 指令 (屏東縣):
curl -X 'GET' \
'[https://opendata.cwa.gov.tw/api/v1/rest/datastore/E-A0015-001?Authorization=您的驗證碼&format=JSON&AreaName=%E5%B1%8F%E6%9D%B1%E7%B8%A3](https://www.google.com/search?q=https://opendata.cwa.gov.tw/api/v1/rest/datastore/E-A0015-001%3FAuthorization%3D%E6%82%A8%E7%9A%84%E9%A9%97%E8%AD%89%E7%A2%BC%26format%3DJSON%26AreaName%3D%25E5%25B1%258F%25E6%259D%25B1%25E7%25B8%25A3)' \
-H 'accept: application/json'
Python 程式碼:
import json
import pandas as pd
def process_earthquake_data(filepath):
"""
處理地震資料 JSON 檔案,進行資料清理和統計。
Args:
filepath: JSON 檔案路徑。
Returns:
一個字典,包含清理後的 DataFrame 和一些統計結果。
"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
data = json.load(f)
except FileNotFoundError:
print(f"錯誤:找不到檔案 {filepath}")
return None
except json.JSONDecodeError:
print(f"錯誤:{filepath} 的 JSON 格式無效")
return None
# 提取地震資料
earthquake_list = []
for earthquake in data['records']['Earthquake']:
earthquake_data = {
'EarthquakeNo': earthquake['EarthquakeNo'],
'ReportType': earthquake['ReportType'],
'ReportColor': earthquake['ReportColor'],
'ReportContent': earthquake['ReportContent'],
'ReportImageURI': earthquake['ReportImageURI'],
'ReportRemark': earthquake['ReportRemark'],
'Web': earthquake['Web'],
'ShakemapImageURI': earthquake['ShakemapImageURI'],
'OriginTime': earthquake['EarthquakeInfo']['OriginTime'],
'Source': earthquake['EarthquakeInfo']['Source'],
'FocalDepth': earthquake['EarthquakeInfo']['FocalDepth'],
'Location': earthquake['EarthquakeInfo']['Epicenter']['Location'],
'EpicenterLatitude': earthquake['EarthquakeInfo']['Epicenter']['EpicenterLatitude'],
'EpicenterLongitude': earthquake['EarthquakeInfo']['Epicenter']['EpicenterLongitude'],
'MagnitudeType': earthquake['EarthquakeInfo']['EarthquakeMagnitude']['MagnitudeType'],
'MagnitudeValue': earthquake['EarthquakeInfo']['EarthquakeMagnitude']['MagnitudeValue'],
}
# 展開 Intensity 資料
for area in earthquake['Intensity']['ShakingArea']:
for station in area.get("EqStation", []):
earthquake_data_copy = earthquake_data.copy()
earthquake_data_copy['AreaDesc'] = area['AreaDesc']
earthquake_data_copy['CountyName'] = area['CountyName']
earthquake_data_copy['AreaIntensity'] = area['AreaIntensity']
earthquake_data_copy['StationName'] = station.get('StationName')
earthquake_data_copy['StationID'] = station.get('StationID')
earthquake_data_copy['SeismicIntensity'] = station.get('SeismicIntensity')
earthquake_data_copy['WaveImageURI'] = station.get('WaveImageURI')
earthquake_data_copy['BackAzimuth'] = station.get('BackAzimuth')
earthquake_data_copy['EpicenterDistance'] = station.get('EpicenterDistance')
earthquake_data_copy['StationLatitude'] = station.get('StationLatitude')
earthquake_data_copy['StationLongitude'] = station.get('StationLongitude')
if 'pga' in station:
earthquake_data_copy['pga_EWComponent'] = station['pga'].get('EWComponent')
earthquake_data_copy['pga_NSComponent'] = station['pga'].get('NSComponent')
earthquake_data_copy['pga_VComponent'] = station['pga'].get('VComponent')
earthquake_data_copy['pga_IntScaleValue'] = station['pga'].get('IntScaleValue')
earthquake_data_copy['pga_unit'] = station['pga'].get('unit')
if 'pgv' in station:
earthquake_data_copy['pgv_EWComponent'] = station['pgv'].get('EWComponent')
earthquake_data_copy['pgv_NSComponent'] = station['pgv'].get('NSComponent')
earthquake_data_copy['pgv_VComponent'] = station['pgv'].get('VComponent')
earthquake_data_copy['pgv_IntScaleValue'] = station['pgv'].get('IntScaleValue')
earthquake_data_copy['pgv_unit'] = station['pgv'].get('unit')
earthquake_list.append(earthquake_data_copy)
if len(area.get("EqStation", [])) == 0:
earthquake_data_copy = earthquake_data.copy()
earthquake_data_copy['AreaDesc'] = area['AreaDesc']
earthquake_data_copy['CountyName'] = area['CountyName']
earthquake_data_copy['AreaIntensity'] = area['AreaIntensity']
earthquake_list.append(earthquake_data_copy)
# 建立 DataFrame
df = pd.DataFrame(earthquake_list)
# 資料清理
# 1. 轉換 OriginTime 為 datetime
df['OriginTime'] = pd.to_datetime(df['OriginTime'])
# 2. 轉換數值欄位為數值類型
numeric_cols = ['FocalDepth', 'EpicenterLatitude', 'EpicenterLongitude', 'MagnitudeValue',
'pga_EWComponent', 'pga_NSComponent', 'pga_VComponent', 'pga_IntScaleValue',
'pgv_EWComponent', 'pgv_NSComponent', 'pgv_VComponent', 'pgv_IntScaleValue',
'BackAzimuth','EpicenterDistance','StationLatitude','StationLongitude'
]
for col in numeric_cols:
if col in df.columns:
df[col] = pd.to_numeric(df[col], errors='coerce')
# 3. 處理缺失值
for col in numeric_cols:
if col in df.columns:
df[col].fillna(df[col].mean(), inplace=True)
for col in df.select_dtypes(include=['object']).columns:
df[col].fillna(df[col].mode()[0], inplace=True)
# 統計分析
# 1. 平均地震規模
avg_magnitude = df['MagnitudeValue'].mean()
# 2. 最大震度的地區
max_intensity_area = df.groupby('AreaIntensity')['EarthquakeNo'].count().idxmax()
# 3. 每個縣市的地震次數
earthquake_count_per_county = df['CountyName'].value_counts()
# 4. 地震總數
total_earthquake_num = len(df.drop_duplicates(subset="EarthquakeNo"))
#5. 最大PGA 的測站
if 'pga_IntScaleValue' in df.columns:
max_pga_station = df.loc[df['pga_IntScaleValue'].idxmax()]
else:
max_pga_station = None
#6. 最大PGV的測站
if 'pgv_IntScaleValue' in df.columns:
max_pgv_station = df.loc[df['pgv_IntScaleValue'].idxmax()]
else:
max_pgv_station = None
return {
'cleaned_df': df,
'average_magnitude': avg_magnitude,
'max_intensity_area': max_intensity_area,
'earthquake_count_per_county': earthquake_count_per_county,
'total_earthquake_num':total_earthquake_num,
'max_pga_station':max_pga_station,
'max_pgv_station':max_pgv_station,
}
# 範例使用
filepath = 'api_earthquake.json'
results = process_earthquake_data(filepath)
if results:
print("清理後的 DataFrame:")
print(results['cleaned_df'])
print("\n統計結果:")
print(f"平均地震規模:{results['average_magnitude']:.2f}")
print(f"最大震度地區:{results['max_intensity_area']}")
print(f"每個縣市的地震次數:\n{results['earthquake_count_per_county']}")
print(f"地震總數(獨立編號):{results['total_earthquake_num']}")
if results['max_pga_station'] is not None:
print(f"\n最大 PGA 的測站:")
print(results['max_pga_station'])
if results['max_pgv_station'] is not None:
print(f"\n最大 PGV 的測站:")
print(results['max_pgv_station'])
import json
import pandas as pd
import json
: 導入 Python 的 json
模組,用於處理 JSON (JavaScript Object Notation) 格式的資料。JSON 是一種輕量級的資料交換格式,常用于 Web API 數據傳輸。import pandas as pd
: 導入 pandas
函式庫並簡稱為 pd
。 pandas
是一個強大的資料分析函式庫,提供 DataFrame
資料結構,便於進行資料清理、轉換與分析。process_earthquake_data
函式 def process_earthquake_data(filepath):
"""
處理地震資料 JSON 檔案,進行資料清理和統計。
Args:
filepath: JSON 檔案路徑。
Returns:
一個字典,包含清理後的 DataFrame 和一些統計結果。
"""
# ... 程式碼 ...
def process_earthquake_data(filepath):
: 定義名為 process_earthquake_data
的函式,接受 filepath
參數,代表地震資料 JSON 檔案的路徑。"""..."""
: 函式的docstring,用於說明函式功能、參數及回傳值,提升程式碼可讀性與維護性。
Args:
: 說明函式接受的參數,filepath
為 JSON 檔案路徑。Returns:
: 說明函式回傳值,為一字典,包含清理後的 DataFrame
及統計結果。 try:
with open(filepath, 'r', encoding='utf-8') as f:
data = json.load(f)
except FileNotFoundError:
print(f"錯誤:找不到檔案 {filepath}")
return None
except json.JSONDecodeError:
print(f"錯誤:{filepath} 的 JSON 格式無效")
return None
try...except
: Python 的錯誤處理機制。try
區塊內程式碼會先執行,若發生錯誤,則會跳至 except
區塊執行相應的錯誤處理。with open(filepath, 'r', encoding='utf-8') as f:
: 使用 with open()
語法安全地開啟檔案。
filepath
: 檔案路徑。'r'
: 以讀取模式開啟檔案。encoding='utf-8'
: 使用 UTF-8 編碼讀取檔案,確保能正確處理包含中文的 JSON 檔案。as f
: 將檔案物件賦值給變數 f
。 with
語法確保檔案使用完畢後自動關閉,即使發生錯誤亦然。data = json.load(f)
: 使用 json.load(f)
將檔案 f
中的 JSON 資料讀取到 data
變數中。except FileNotFoundError:
: 捕捉 FileNotFoundError
錯誤,即檔案不存在。若發生此錯誤,印出錯誤訊息並回傳 None
。except json.JSONDecodeError:
: 捕捉 json.JSONDecodeError
錯誤,即 JSON 格式無效。若發生此錯誤,印出錯誤訊息並回傳 None
。 # 提取地震資料
earthquake_list = []
for earthquake in data['records']['Earthquake']:
earthquake_data = {
'EarthquakeNo': earthquake['EarthquakeNo'],
'ReportType': earthquake['ReportType'],
'ReportColor': earthquake['ReportColor'],
'ReportContent': earthquake['ReportContent'],
'ReportImageURI': earthquake['ReportImageURI'],
'ReportRemark': earthquake['ReportRemark'],
'Web': earthquake['Web'],
'ShakemapImageURI': earthquake['ShakemapImageURI'],
'OriginTime': earthquake['EarthquakeInfo']['OriginTime'],
'Source': earthquake['EarthquakeInfo']['Source'],
'FocalDepth': earthquake['EarthquakeInfo']['FocalDepth'],
'Location': earthquake['EarthquakeInfo']['Epicenter']['Location'],
'EpicenterLatitude': earthquake['EarthquakeInfo']['Epicenter']['EpicenterLatitude'],
'EpicenterLongitude': earthquake['EarthquakeInfo']['Epicenter']['EpicenterLongitude'],
'MagnitudeType': earthquake['EarthquakeInfo']['EarthquakeMagnitude']['MagnitudeType'],
'MagnitudeValue': earthquake['EarthquakeInfo']['EarthquakeMagnitude']['MagnitudeValue'],
}
# 展開 Intensity 資料
for area in earthquake['Intensity']['ShakingArea']:
for station in area.get("EqStation", []):
earthquake_data_copy = earthquake_data.copy()
earthquake_data_copy['AreaDesc'] = area['AreaDesc']
earthquake_data_copy['CountyName'] = area['CountyName']
earthquake_data_copy['AreaIntensity'] = area['AreaIntensity']
earthquake_data_copy['StationName'] = station.get('StationName')
earthquake_data_copy['StationID'] = station.get('StationID')
earthquake_data_copy['SeismicIntensity'] = station.get('SeismicIntensity')
earthquake_data_copy['WaveImageURI'] = station.get('WaveImageURI')
earthquake_data_copy['BackAzimuth'] = station.get('BackAzimuth')
earthquake_data_copy['EpicenterDistance'] = station.get('EpicenterDistance')
earthquake_data_copy['StationLatitude'] = station.get('StationLatitude')
earthquake_data_copy['StationLongitude'] = station.get('StationLongitude')
if 'pga' in station:
earthquake_data_copy['pga_EWComponent'] = station['pga'].get('EWComponent')
earthquake_data_copy['pga_NSComponent'] = station['pga'].get('NSComponent')
earthquake_data_copy['pga_VComponent'] = station['pga'].get('VComponent')
earthquake_data_copy['pga_IntScaleValue'] = station['pga'].get('IntScaleValue')
earthquake_data_copy['pga_unit'] = station['pga'].get('unit')
if 'pgv' in station:
earthquake_data_copy['pgv_EWComponent'] = station['pgv'].get('EWComponent')
earthquake_data_copy['pgv_NSComponent'] = station['pgv'].get('NSComponent')
earthquake_data_copy['pgv_VComponent'] = station['pgv'].get('VComponent')
earthquake_data_copy['pgv_IntScaleValue'] = station['pgv'].get('IntScaleValue')
earthquake_data_copy['pgv_unit'] = station['pgv'].get('unit')
earthquake_list.append(earthquake_data_copy)
if len(area.get("EqStation", [])) == 0:
earthquake_data_copy = earthquake_data.copy()
earthquake_data_copy['AreaDesc'] = area['AreaDesc']
earthquake_data_copy['CountyName'] = area['CountyName']
earthquake_data_copy['AreaIntensity'] = area['AreaIntensity']
earthquake_list.append(earthquake_data_copy)
earthquake_list = []
: 建立空列表 earthquake_list
,用於儲存提取出的地震資料。for earthquake in data['records']['Earthquake']:
: 迴圈遍歷 data['records']['Earthquake']
中的每個地震資料。earthquake_data = {...}
: 建立字典 earthquake_data
,儲存單個地震的基本資訊。
key
為欄位名稱,value
從 JSON 資料中提取。例如 'EarthquakeNo': earthquake['EarthquakeNo']
將 JSON 資料中 earthquake['EarthquakeNo']
的值存入字典,鍵為 'EarthquakeNo'
。for area in earthquake['Intensity']['ShakingArea']:
: 迴圈遍歷每個地震的 Intensity
中的 ShakingArea
,ShakingArea
包含特定區域的震度資訊。for station in area.get("EqStation", []):
: 迴圈遍歷每個 ShakingArea
中的 EqStation
(地震站)。使用 .get("EqStation", [])
在 EqStation
不存在時提供空列表,避免程式錯誤。earthquake_data_copy = earthquake_data.copy()
: 複製原始地震資料至 earthquake_data_copy
,以便在不影響原始資料下,新增或修改站點特定資訊。earthquake_data_copy['AreaDesc'] = area['AreaDesc']
… earthquake_data_copy['AreaIntensity'] = area['AreaIntensity']
: 將區域描述、縣市名稱及區域震度等資訊加入 earthquake_data_copy
字典。earthquake_data_copy['StationName'] = station.get('StationName')
… earthquake_data_copy['SeismicIntensity'] = station.get('SeismicIntensity')
: 從 station
字典提取站點名稱、ID 及地震強度等資訊,加入 earthquake_data_copy
字典。使用 .get()
方法安全處理可能不存在的鍵。if 'pga' in station:
和 if 'pgv' in station:
: 檢查 station
字典是否存在 'pga'
(峰值地面加速度) 或 'pgv'
(峰值地面速度) 資訊。earthquake_data_copy['pga_EWComponent'] = station['pga'].get('EWComponent')
… earthquake_data_copy['pga_unit'] = station['pga'].get('unit')
: 若存在 'pga'
資訊,提取東-西、南-北、垂直方向分量、強度比例值及單位,並加入 earthquake_data_copy
字典。使用 .get()
處理可能缺失的鍵。earthquake_data_copy['pgv_EWComponent'] = station['pgv'].get('EWComponent')
… earthquake_data_copy['pgv_unit'] = station['pgv'].get('unit')
: 若存在 'pgv'
資訊,提取東-西、南-北、垂直方向分量、強度比例值及單位,並加入 earthquake_data_copy
字典。earthquake_list.append(earthquake_data_copy)
: 將包含地震與站點特定資訊的 earthquake_data_copy
字典加入 earthquake_list
列表。if len(area.get("EqStation", [])) == 0:
: 檢查 area
中是否存地震站 (EqStation
)。若不存在 (長度為 0),執行後續程式碼,處理區域可能缺少地震站資料情況,仍將區域描述、縣市名稱、區域震度等資訊加入 earthquake_list
,即使缺乏具體站點資料。earthquake_list.append(earthquake_data_copy)
: 將包含地震與區域資訊的 earthquake_data_copy
字典加入 earthquake_list
列表。 # 建立 DataFrame
df = pd.DataFrame(earthquake_list)
df = pd.DataFrame(earthquake_list)
: 使用 pandas.DataFrame()
函式將 earthquake_list
轉換為 DataFrame
。DataFrame
是一種表格型資料結構,方便後續資料清理與分析。 # 資料清理
# 1. 轉換 OriginTime 為 datetime
df['OriginTime'] = pd.to_datetime(df['OriginTime'])
# 2. 轉換數值欄位為數值類型
numeric_cols = ['FocalDepth', 'EpicenterLatitude', 'EpicenterLongitude', 'MagnitudeValue',
'pga_EWComponent', 'pga_NSComponent', 'pga_VComponent', 'pga_IntScaleValue',
'pgv_EWComponent', 'pgv_NSComponent', 'pgv_VComponent', 'pgv_IntScaleValue',
'BackAzimuth','EpicenterDistance','StationLatitude','StationLongitude'
]
for col in numeric_cols:
if col in df.columns:
df[col] = pd.to_numeric(df[col], errors='coerce')
# 3. 處理缺失值
for col in numeric_cols:
if col in df.columns:
df[col].fillna(df[col].mean(), inplace=True)
for col in df.select_dtypes(include=['object']).columns:
df[col].fillna(df[col].mode()[0], inplace=True)
df['OriginTime'] = pd.to_datetime(df['OriginTime'])
: 將 OriginTime
欄位轉換為 datetime
格式。 pd.to_datetime()
函式可將字串或數字轉為 datetime
格式,便於時間序列分析。numeric_cols = [...]
: 定義列表 numeric_cols
,包含需轉換為數值類型的欄位名稱。for col in numeric_cols:
: 迴圈遍歷 numeric_cols
中的每個欄位名稱。
if col in df.columns:
: 檢查 DataFrame
是否存在該欄位。df[col] = pd.to_numeric(df[col], errors='coerce')
: 使用 pd.to_numeric()
函式將欄位轉為數值類型。errors='coerce'
設定轉換失敗時設為 NaN
(缺失值)。for col in numeric_cols:
: 迴圈遍歷 numeric_cols
中的每個欄位名稱。
if col in df.columns:
: 檢查 DataFrame
是否存在該欄位。df[col].fillna(df[col].mean(), inplace=True)
: 使用平均值填補數值欄位的缺失值 (NaN
)。fillna()
函式用於填補缺失值,df[col].mean()
計算欄位平均值, inplace=True
直接修改 DataFrame
。for col in df.select_dtypes(include=['object']).columns:
: 迴圈遍歷 DataFrame
中所有字串類型的欄位。
df[col].fillna(df[col].mode()[0], inplace=True)
: 使用眾數填補字串類型欄位的缺失值。 df[col].mode()[0]
計算欄位眾數,inplace=True
直接修改 DataFrame
。 # 統計分析
# 1. 平均地震規模
avg_magnitude = df['MagnitudeValue'].mean()
# 2. 最大震度的地區
max_intensity_area = df.groupby('AreaIntensity')['EarthquakeNo'].count().idxmax()
# 3. 每個縣市的地震次數
earthquake_count_per_county = df['CountyName'].value_counts()
# 4. 地震總數
total_earthquake_num = len(df.drop_duplicates(subset="EarthquakeNo"))
#5. 最大PGA 的測站
if 'pga_IntScaleValue' in df.columns:
max_pga_station = df.loc[df['pga_IntScaleValue'].idxmax()]
else:
max_pga_station = None
#6. 最大PGV的測站
if 'pgv_IntScaleValue' in df.columns:
max_pgv_station = df.loc[df['pgv_IntScaleValue'].idxmax()]
else:
max_pgv_station = None
avg_magnitude = df['MagnitudeValue'].mean()
: 計算平均地震規模。df['MagnitudeValue']
選擇 MagnitudeValue
欄位,.mean()
計算平均值。max_intensity_area = df.groupby('AreaIntensity')['EarthquakeNo'].count().idxmax()
: 找出最大震度的地區。
df.groupby('AreaIntensity')
: 依 AreaIntensity
欄位分組。['EarthquakeNo'].count()
: 計算每組別中 EarthquakeNo
的數量。.idxmax()
: 找出數量最多組別的名稱 (即最大震度地區)。earthquake_count_per_county = df['CountyName'].value_counts()
: 統計每個縣市的地震次數。 df['CountyName']
選擇 CountyName
欄位,.value_counts()
計算各縣市出現次數。total_earthquake_num = len(df.drop_duplicates(subset="EarthquakeNo"))
: 計算地震總數 (獨立編號),以 EarthquakeNo
去除重複地震。
df.drop_duplicates(subset="EarthquakeNo")
: 移除 EarthquakeNo
欄位值重複的列,僅保留首次出現的列。len(...)
: 計算 DataFrame
列數,即地震總數。if 'pga_IntScaleValue' in df.columns:
: 檢查 DataFrame
是否存在 pga_IntScaleValue
欄位。
max_pga_station = df.loc[df['pga_IntScaleValue'].idxmax()]
: 找出最大 PGA 的測站資訊。
df['pga_IntScaleValue'].idxmax()
: 找出 pga_IntScaleValue
欄位最大值的索引。df.loc[...]
: 使用索引選擇 DataFrame
相應列,即最大 PGA 測站資訊。else: max_pga_station = None
: 若 DataFrame
無 pga_IntScaleValue
欄位,max_pga_station
設為 None
。if 'pgv_IntScaleValue' in df.columns:
: 檢查 DataFrame
是否存在 pgv_IntScaleValue
欄位。
max_pgv_station = df.loc[df['pgv_IntScaleValue'].idxmax()]
: 找出最大 PGV 的測站資訊。
df['pgv_IntScaleValue'].idxmax()
: 找出 pgv_IntScaleValue
欄位最大值的索引。df.loc[...]
: 使用索引選擇 DataFrame
相應列,即最大 PGV 測站資訊。else: max_pgv_station = None
: 若 DataFrame
無 pgv_IntScaleValue
欄位,max_pgv_station
設為 None
。 return {
'cleaned_df': df,
'average_magnitude': avg_magnitude,
'max_intensity_area': max_intensity_area,
'earthquake_count_per_county': earthquake_count_per_county,
'total_earthquake_num':total_earthquake_num,
'max_pga_station':max_pga_station,
'max_pgv_station':max_pgv_station,
}
return {...}
: 將清理後的 DataFrame
及統計結果以字典形式回傳。字典的 key
為結果名稱,value
為對應值。# 範例使用
filepath = '/Users/aaron/Downloads/api_earthquake.json'
results = process_earthquake_data(filepath)
if results:
print("清理後的 DataFrame:")
print(results['cleaned_df'])
print("\n統計結果:")
print(f"平均地震規模:{results['average_magnitude']:.2f}")
print(f"最大震度地區:{results['max_intensity_area']}")
print(f"每個縣市的地震次數:\n{results['earthquake_count_per_county']}")
print(f"地震總數(獨立編號):{results['total_earthquake_num']}")
if results['max_pga_station'] is not None:
print(f"\n最大 PGA 的測站:")
print(results['max_pga_station'])
if results['max_pgv_station'] is not None:
print(f"\n最大 PGV 的測站:")
print(results['max_pgv_station'])
filepath = '/Users/aaron/Downloads/api_earthquake.json'
: 設定 JSON 檔案路徑。請確保路徑正確指向您的地震資料 JSON 檔案。results = process_earthquake_data(filepath)
: 呼叫 process_earthquake_data()
函式處理資料,結果存入 results
變數。if results:
: 檢查 results
是否為 None
。若非 None
,表示資料處理成功,執行後續程式碼印出結果。print("清理後的 DataFrame:")
print(results['cleaned_df'])
: 印出清理後的 DataFrame。print("\n統計結果:")
: 印出統計結果標題。print(f"平均地震規模:{results['average_magnitude']:.2f}")
: 印出平均地震規模。f
表示 f-string,用於格式化字串。 :.2f
保留兩位小數。print(f"最大震度地區:{results['max_intensity_area']}")
: 印出最大震度地區。print(f"每個縣市的地震次數:\n{results['earthquake_count_per_county']}")
: 印出每個縣市的地震次數。\n
表示換行。print(f"地震總數(獨立編號):{results['total_earthquake_num']}")
: 印出地震總數 (獨立編號)。if results['max_pga_station'] is not None:
: 檢查 results['max_pga_station']
是否為 None
。非 None
表示找到最大 PGA 站點,執行後續程式碼。
print(f"\n最大 PGA 的測站:")
print(results['max_pga_station'])
: 印出 最大 PGA 測站資訊。if results['max_pgv_station'] is not None:
: 檢查 results['max_pgv_station']
是否為 None
。非 None
表示找到最大 PGV 站點,執行後續程式碼。
print(f"\n最大 PGV 的測站:")
print(results['max_pgv_station'])
: 印出 最大 PGV 測站資訊。你正在修一門 資料科學課程,但在「資料收集」這一環節遇到了困難。這裡會以 第一性原理 拆解你的問題,並給出清晰的學習與實作方向。
我們將探討了多種資料科學應用,涵蓋 開放數據擷取、API 介接、網頁爬蟲、資料處理與儲存技術。以下是摘要整理:
這些技術涵蓋 金融、電商、健康、學術研究 等領域,展示如何利用資料科學與 AI 工具來高效擷取、處理與分析數據,進一步實現自動化應用。
2025-02-23課程簡介: 本課程旨在深入探討資料科學的基礎工具與應用,從資料處理、資料清理,到API應用、金融資料分析等,逐步帶領學生熟悉如何使用Python工具解決實際的資料科學問題。
to_datetime
函式轉換日期yfinance
來抓取金融市場資料並進行基本分析。yfinance
庫課程總結: 通過本課程的學習,掌握資料處理、API應用、資料清理及基本的資料分析技巧,並能將所學應用於真實的資料科學項目中,為未來的數據分析職業生涯打下堅實的基礎。
2025-02-23import requests
import json
import pandas as pd
import requests
: 就像準備好了一支「網路請求工具」。 requests 是一個 Python 的函式庫,專門用來發送網路請求,例如我們今天要用的 GET 請求,去跟資料來源 (農業部的 API) 要資料。想像一下,你要去餐廳點餐, requests 就是幫你把菜單送到廚房的工具。
import json
: 這個是「JSON 資料處理工具」。JSON 是一種常見的資料格式,很像字典或列表,但它是文字格式,方便在網路傳輸。API 回傳的資料通常都是 JSON 格式。 json 函式庫可以幫我們把 JSON 格式的文字轉換成 Python 可以讀懂的字典或列表,反之亦然。就像翻譯機,幫我們把外文翻譯成中文。
import pandas as pd
: 這是「資料分析的瑞士刀」! pandas 是 Python 最強大的資料分析函式庫之一。它提供了一個叫做 DataFrame 的資料結構,可以讓我們像操作表格一樣,輕鬆地整理、清理、分析資料。 pd
是 pandas 的簡稱,之後我們用 pd
就可以代表 pandas 這個函式庫了。
pykml
(或 xml.etree.ElementTree
)、requests
。map.kml
。import csv
import xml.etree.ElementTree as ET
# 讀取 KML 檔案
kml_file = "map.kml"
tree = ET.parse(kml_file)
root = tree.getroot()
# 定義 KML 命名空間
namespace = {"k": "http://www.opengis.net/kml/2.2"}
# 找出所有地點
places = root.findall(".//k:Placemark", namespace)
# 存儲資料
data = []
for place in places:
name = place.find("k:name", namespace).text
coordinates = place.find(".//k:coordinates", namespace).text.strip()
lon, lat, _ = coordinates.split(",")
data.append([name, lat, lon])
# 輸出 CSV
csv_filename = "google_map_data.csv"
with open(csv_filename, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["Name", "Latitude", "Longitude"])
writer.writerows(data)
print(f"已成功將 KML 轉換為 CSV:{csv_filename}")
google_map_data.csv
,確認資料正確。import requests
import csv
API_KEY = "你的API金鑰"
LOCATION = "25.0330,121.5654" # 台北101
RADIUS = 5000 # 搜尋半徑
TYPE = "restaurant" # 可改為其他類型
url = f"https://maps.googleapis.com/maps/api/place/nearbysearch/json?location={LOCATION}&radius={RADIUS}&type={TYPE}&key={API_KEY}"
response = requests.get(url)
data = response.json()
# 存成 CSV
csv_filename = "google_places.csv"
with open(csv_filename, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["Name", "Latitude", "Longitude", "Address"])
for place in data["results"]:
name = place["name"]
lat = place["geometry"]["location"]["lat"]
lon = place["geometry"]["location"]["lng"]
address = place.get("vicinity", "N/A")
writer.writerow([name, lat, lon, address])
print(f"已成功擷取地點資料並存為 CSV:{csv_filename}")
google_places.csv
,確認包含地點名稱與座標。google_map_data.csv
(方法 1)。google_places.csv
(方法 2)。第一性原理(First Principles Thinking)簡介 第一性原理是一種思維方法,核心在於將問題拆解到最基本、不可再簡化的原則,然後從這些基礎原則重新構建解決方案,而非依賴現有的經驗或假設。這種方法常用於科學、工程、創新及商業決策,例如伊隆·馬斯克(Elon Musk)在設計 SpaceX 火箭時,便應用了這種思維來降低製造成本。
第一性原理的 7 個步驟 確定問題
明確定義你要解決的問題,確保問題本身不是基於假設或傳統思維,而是從最根本的層面來思考。 拆解現有假設
檢視現有解法或傳統觀點,並挑戰其中的假設。例如,馬斯克認為「火箭必須昂貴」這個假設值得被質疑。 分解到最基本元素(第一性原理)
把問題拆解到最基本的物理定律、數學原則或不可再簡化的事實。例如,火箭的基本組成是材料(如鋁、碳纖維、燃料)。 建立新組合或解決方案
根據第一性原理,重新思考可能的解決方案,而不是沿用現有的做法。例如,SpaceX 直接購買原材料製造火箭,而非購買昂貴的成品。 驗證可行性
透過數據、實驗或計算,確認新的解決方案是否實際可行。例如,計算製造火箭的成本是否低於市場價格。 實施並快速迭代
開始執行並進行快速測試與優化,持續改進方案,確保它能夠解決問題。例如,SpaceX 透過多次測試改進火箭技術。 持續反思與挑戰現狀
第一性原理並非一次性的過程,而是一種思維習慣,需要不斷挑戰現有解法,確保最佳解決方案持續演進。
2025-02-23本報告利用資料科學方法,從「車床天地」的Google Map KML檔案提取數據,結合台灣政府開放資料平台(https://data.gov.tw)中的多項資料集(包括內政部的宮廟資料),設計一套適用於台灣環境的戰時避難疏散路線指引系統。系統基於車泊點與宮廟資料,加入避難相關元數據(如可容納人數、即時收容人數),並採用MongoDB作為資料庫,實現高效儲存與查詢。本報告刻意排除民防系統常見的學校與公園作為主要避難場域,並解釋其理由。內容涵蓋資料提取與清理、Metadata設計、路線規劃邏輯、資料庫設計,以及未來擴展建議,旨在提供一個具實用性與可擴展性的避難解決方案。
利用資料科學技術,將車床天地KML檔案中的車泊點數據與政府開放資料(特別是內政部的宮廟資料)整合,設計戰時避難疏散路線指引系統。目標包括:
從政府開放資料平台(https://data.gov.tw)獲取以下相關資料集,補充系統資訊:
2025-02-23這十幾年來,文件和筆記散落在各大協作平台(Notion、Slack、Asana、Trello、HackMD、Evernote…),現在是時候搬家了!
考慮到這些平台即使是不公開的內容,仍有可能被賣給廣告商或用於 AI 訓練,Anytype 提供了一種真正 私密、安全、去中心化 的解決方案,讓你的數據掌控權回歸自己手中。
功能特色 | Anytype | Notion | Obsidian | Logseq |
---|---|---|---|---|
本地優先存儲 | ✅ 是 | ❌ 否 | ✅ 是 | ✅ 是 |
P2P 去中心化 | ✅ 是 | ❌ 否 | ❌ 否 | ✅ 是 |
端對端加密 | ✅ 是 | ❌ 否 | ❌ 否 | ✅ 是 |
自訂資料結構 | ✅ 高 | ✅ 中 | ❌ 低 | ❌ 低 |
跨裝置同步 | ✅ P2P | ✅ 雲端 | ✅ 本地插件 | ✅ P2P |
API 支援 | ❌ 無 | ✅ 有 | ✅ 有 | ✅ 有 |
目前 Anytype 最大弱點 是 尚未開放 API,但官方已於 2024 年底開始開發,未來支援 API 後,將會是最完整的個人知識管理工具之一!
短期內 Notion 仍可用來處理 API 相關的需求,但當 Anytype API 穩定後,將能完全取代 Notion。