時間是最誠實的口碑——網站做得好不好,看客戶願意跟多久。
網站設計製作
從規劃、設計到上線,同一組人負責到底,時程白紙黑字。
主機代管與資安
網站放我們家,天天備份、全年監控,出狀況主動通知。
搜尋與 AI 曝光
讓 Google 搜得到你,也讓 ChatGPT 推薦你。
交出去的網站,經得起點開來看
從形象官網、電商到客製系統,各行各業的實際成果。
法律、地政
明永大揚聯合法律事務所
首頁採用滿版設計,以天枰底圖搭配深色場景襯托律師事務所應有的穩重感。|希望原值輸出
法律、地政
慶芳地政士聯合事務所
所長李志殷博士是政治大學地政博士,三所大學的兼任助理教授,「不動產登記」期刊主編,在地政領域同時具備最頂層的學術高度與三十年的實務厚度——這種組合,在整個業界幾乎是稀有物種。
運動
再興高爾夫球場
開場於 1994年,是由日本熊谷組在台關係企業華熊公司規劃設計,美國球場設計師高登‧路易斯進行細部整修工程,十八洞從藍梯起算全長7112碼,標準桿72桿。陽光、青山、綠草、大樹、奇石、湖景等無數無價自然資產、在這裡完美的結合在一起,為每一位在此打球者提供最佳的視野享受和揮桿樂趣。
他們把網站交給我們,然後留了下來
從一次合作到長年續約,是客戶用時間投下的信任票。
網站上線七年,中間改版、加購物車、串金流,都是同一位窗口處理,不用每次重新解釋一遍,這點省下我們很多時間。
之前的廠商完全找不到人,NC 接手後把資料全部搬過來,網站兩天就重新開得起來,還幫我們把速度調快了。
從協會成立初期就合作到現在,需要調整內容打通電話就有人回,二十幾年下來,這份安心是換不掉的。
最新消息與觀點
網站經營、數位行銷與產業趨勢,我們持續分享。
最新訊息
GEO、AEO:取代傳統 SEO 的新說法
GEO(生成引擎最佳化)與 AEO(答案引擎最佳化)是為了 AI 搜尋而生的兩個新名詞,目標是讓你的內容被 ChatGPT、Gemini、Perplexity、Google AI Overview 直接引用,而不只是在藍色連結列表裡排前面。...
詳細內容...
專題文章
乾淨的語意標記與合理的資訊架構,才是讓 AI 引擎能引用你、讓代理能操作你網站的基礎
想被 AI 引擎引用、被 AI 代理正確操作,關鍵不在於你的網站多好看,而在於機器能不能一眼看懂它的骨架。用 semantic HTML 標出每一塊內容的角色、用清楚的標題階層與資訊架構把答案切成可擷取的段落、再用 schema.org 結...
詳細內容...
最新訊息
好運氣 評測|台北桶裝瓦斯數位配送的全新選擇,安全、透明、便利一次到位!
好運氣是台北第一個結合數位預約與新型安全氣瓶的桶裝瓦斯配送品牌,提供線上叫瓦斯、透明計量、餘氣累積等創新服務。本文深入介紹好運氣的品牌理念、服務特色與數位轉型歷程。 說到「叫瓦斯」這件事,大多數人的第一反應可能是:打電話、等師傅、不知道桶子...
詳細內容...
專題文章
搜尋引擎的排名演算法有哪些?意義、關聯性、品質、可用性、背景資訊!建立實用可靠且以使用者為優先的內容!
網路世界的明星——搜尋引擎排名演算法。相信每個上網衝浪的朋友,都好奇過為什麼某些網站能穩穩地坐在搜尋結果的前幾名?別擔心,我們這篇文章將帶你一探究竟!
詳細內容...
專題文章
購物平台、電商、網路開店?讓自有購物網站成為媒體等級吧!
自有購物網站應該是自有資產,由我們設計的購物網站就是您的自有資產,NC網頁設計教您活化自有資產。
詳細內容...
專題文章
深色主題的網頁好處多,為何網頁設計公司的作品還是以淺色居多?
在數位資訊的時代,網頁設計扮演著至關重要的角色。隨著科技的進步,深色主題(Dark Mode)逐漸成為一種流行趨勢。然而,許多網頁設計公司的作品仍然以淺色主題為主。本文將深入探討深色主題的優點,以及為什麼許多網頁設計公司仍偏好淺色設計。
詳細內容...五個步驟,把想法變成上線的網站
每一步都有專人對接、進度看得到,不怕做到一半沒下文。
免費諮詢
先聊需求、目標與預算,不用先付錢,也沒有制式話術。
1 個工作天回覆提案與報價
功能、時程、費用白紙黑字寫清楚,一次看懂再決定。
費用透明設計與確認
先看設計稿再開工,版面、配色、風格確認滿意才往下。
可調整修改開發與測試
切版、寫程式、跨裝置測試,手機電腦都確保正常好用。
RWD 全裝置上線與維護
網站上線只是開始,後續改字換圖、加功能都找得到人。
長期照顧為什麼客戶願意跟我們一路走下去
不是最便宜、也不是最花俏,但你需要時,我們一直都在。
做網站的公司很多, 26 年還在的很少。
從團隊創立至今,同一群人守著同一件事——把客戶的網站顧好。
找得到人
沒有客服機器人繞圈圈,一通電話真人接聽,問題直接找到負責的人。
同一組人負責到底
從設計、開發到後續維護不換手,不用每次重新解釋一遍需求。
桃園在地服務
需要當面談的時候約得到、來得了,不是只有線上冷冰冰的往來。
費用清楚透明
報價白紙黑字寫明白,該多少就多少,不玩事後追加的把戲。
主機是會默默出問題的
網站不會突然壞掉,通常是某個資源慢慢用完——磁碟滿了、記憶體不足、憑證過期、方案到期。
這些都是可以預先發現的,前提是有人在看。
該監控的五個項目
| 項目 | 為什麼重要 | 建議 |
|---|---|---|
| 網站可用性 | 無法開啟時要立即知道 | 設定自動監控與通知 |
| 磁碟用量 | 滿了會讓所有服務停止 | 八成時提醒 |
| 記憶體與負載 | 不足會造成錯誤與緩慢 | 觀察趨勢 |
| 憑證到期日 | 過期會顯示安全警告 | 納入到期日總表 |
| 備份是否產生 | 靜默失敗很常見 | 確認檔案時間戳 |
磁碟是最常見的問題
常見的空間消耗來源:
- 日誌未輪替——最常見
- 資料庫的二進位日誌
- 備份檔案累積
- 上傳的圖片與附件
- 快取與暫存檔案
磁碟滿了的症狀通常是「網站突然壞掉」,而且錯誤訊息可能與磁碟無關,容易誤判。
備份只監控失敗是不夠的
這一點值得單獨強調:如果備份排程根本沒有執行,就不會有失敗通知。
正確的確認方式
- 檢查備份檔案的時間戳是否為最新
- 檢查檔案大小是否合理——異常小代表可能失敗
- 設定「超過 N 小時未產生新備份就通知」
- 每半年實際還原一次驗證
沒有測試過的備份不算備份。
到期日總表
網站相關的到期項目分散在不同服務商,很容易漏掉。建議做一張總表:
| 項目 | 服務商 | 到期日 | 負責人 |
|---|---|---|---|
| 網域 | |||
| 主機 | |||
| SSL 憑證 | |||
| 企業信箱 | |||
| 付費外掛或授權 | |||
| 維護方案 |
兩個實務提醒
一、通知信箱要有人看。 到期通知常常寄到當初申請時留的信箱,而那可能是離職員工的。
二、指定負責人與代理人。 只有一個人知道,那個人休假時就沒人處理。
網域過期的後果最嚴重——不只網站,連信件都會中斷。見網域忘記續約會怎樣。
日常的維護項目
每月
- 確認備份正常產生
- 查看磁碟用量趨勢
- 檢視錯誤日誌有無異常
- 確認網站各項功能正常
每季
- 檢視系統與套件的更新狀態
- 檢查憑證與各項到期日
- 清理過期的日誌與備份
- 檢視資源用量是否接近上限
每半年
- 實際還原一次備份驗證
- 檢視主機方案是否仍符合需求
- 檢查帳號權限,移除不再需要的
- 確認程式語言版本的支援狀態
什麼時候該擴充
明確的訊號
- 磁碟持續接近上限——且已清理過不必要的檔案
- 流量成長後才開始變慢——原本正常,訪客變多就撐不住
- 主機商通知資源用量超標
- 經常出現連線錯誤或逾時
- 純文字頁面的回應也很慢——代表瓶頸在伺服器端
擴充前先確認不是其他問題
很多「主機不夠力」其實是網站本身的問題:
- 圖片沒有壓縮
- 資料庫查詢缺少索引
- 安裝了過多的外掛
- 程式在迴圈中重複查詢
先排除這些,再考慮升級。 排查順序見網站慢的常見原因與優先處理順序。
擴充的方式
- 同一方案升級規格——最單純,通常只需重開機
- 換到更高階的方案——可能需要搬遷
- 把服務分開——例如資料庫獨立、圖片放到專門的儲存服務
- 加上內容傳遞網路——分擔靜態檔案的流量
第三項對圖片多的網站特別有效——把靜態檔案移出主機,能明顯降低負載。
換主機商的判斷
值得換的情況:
- 效能問題持續且反映後未改善
- 支援回應太慢或品質不佳
- 長期停留在舊的程式語言版本
- 經常性的不穩定
- 續約價格明顯不合理
不值得為了小幅的價差而換——搬遷本身有工時與風險成本。
搬遷流程見主機搬遷的完整流程。
帳號與資訊的保管
這是人員異動時最常斷掉的環節。應保存在公司的共用文件中:
- 主機商名稱、控制台網址、帳號
- 網域註冊商與管理帳號
- 資料庫的連線資訊
- 檔案傳輸的帳號
- 各項到期日
- 技術支援的聯絡方式
不要只存在承辦人的個人電腦或瀏覽器裡。 權限管理的原則見後台帳號與權限怎麼規劃。
維護責任要講清楚
不論自管或委外,都應該明確約定:
- 系統更新由誰負責
- 備份由誰執行、存在哪裡
- 監控的範圍與通知對象
- 故障時的回應時間
- 費用怎麼計算
沒有講清楚時,最常見的結果是各方都以為別人在負責。
各主機商的方案內容、規格定義與限制條件差異很大,本文說明的是評估的原則與該確認的項目。實際規格與條款請以各服務商的現行公告為準。
搬遷的完整階段
主機搬遷不只是把檔案複製過去。完整的流程分五個階段:
- 盤點與準備
- 新環境建置
- 資料搬移
- 測試驗證
- 切換與善後
多數搬遷失敗的原因,出在第一階段沒做完整。
第一階段:盤點
把目前主機上的所有東西列出來。常被遺漏的項目:
| 項目 | 容易遺漏的部分 |
|---|---|
| 網站檔案 | 隱藏檔案、上傳目錄 |
| 資料庫 | 預存程序、觸發器 |
| 電子郵件 | 信箱內容、轉寄與別名設定 |
| 排程工作 | 經常被完全忘記 |
| DNS 記錄 | 驗證用的記錄、郵件相關記錄 |
| 憑證 | 續期的設定 |
| 系統設定 | 程式語言版本、擴充套件、上傳限制 |
三個最常被忘記的
排程工作——搬遷後才發現每日的自動作業沒有跑,通常是幾天後才被發現。
郵件——如果信箱在同一台主機,搬遷等於也要搬信件。這比網站複雜得多。
DNS 中的驗證記錄——搜尋主控台、網域驗證、寄件驗證的設定若沒帶過去,會靜默失效。
第二階段:新環境建置
環境要盡量一致
- 程式語言版本——不同版本可能造成程式錯誤
- 資料庫版本與字元集
- 必要的擴充套件
- 上傳大小、執行時間等限制
建議在搬遷時不要同時升級版本。 兩件事一起做,出問題時難以判斷是搬遷還是升級造成的。
先搬遷、確認穩定後,再另外規劃升級。
先申請憑證
在切換 DNS 之前,就要在新主機完成憑證的安裝。
否則切換後會出現安全警告,訪客會直接離開。見SSL 憑證怎麼安裝與續期。
第三階段:資料搬移
檔案
- 保留檔案的權限與擁有者
- 注意隱藏檔案——設定檔常常是隱藏的
- 搬移後比對檔案數量與總大小
資料庫
- 匯出時明確指定字元集
- 包含預存程序、觸發器與排程事件
- 匯入後確認筆數與中文顯示正常
備份與還原的技術細節見資料庫備份與還原的技術要點。
調整設定檔
網站的資料庫連線設定、路徑設定需要更新。建議搬移前先確認設定檔的位置。
第四階段:測試(最重要)
在切換 DNS 之前就要完成測試,方法是修改本機的 hosts 檔案,讓自己的電腦連到新主機。
這樣可以在不影響任何訪客的情況下,完整測試新環境。
測試清單
- 首頁與主要頁面正常顯示
- 後台可以登入與操作
- 中文顯示正常——沒有亂碼
- 圖片與檔案都能正常顯示與下載
- 表單可以送出,且收得到通知信
- 搜尋功能正常
- 會員登入與訂單功能正常
- 金流測試(若有)
- 手機上正常
- 憑證正常,沒有安全警告
第五項最容易出問題——新主機的寄件設定與驗證可能還沒完成。
第五階段:切換
切換前的準備
- 提前降低 DNS 的存活時間——建議提前一到兩天調低,讓切換能快速生效
- 選擇離峰時段
- 暫停會產生新資料的作業——避免資料分散在兩台主機
- 做最後一次資料同步
DNS 的切換操作見網站搬家的 DNS 切換流程。
雙主機並存期
DNS 擴散期間,部分訪客會連到舊主機,部分連到新主機。
這代表:
- 期間新增的資料可能分散在兩邊
- 有訂單或會員功能的網站要特別注意
處理方式
- 選在最低流量的時段切換
- 舊主機暫時設為唯讀或顯示維護訊息
- 切換後檢查舊主機是否還有新資料,必要時手動補齊
切換後的確認
當天
- 各項功能再測一次
- 從外部信箱測試表單通知
- 確認憑證正常
- 查看錯誤日誌
第一週
- 確認排程工作有正常執行
- 確認備份機制運作中
- 觀察錯誤日誌
- 確認搜尋引擎的收錄狀況沒有異常
舊主機不要立刻退租
建議保留至少一到兩週。 理由:
- 可能發現遺漏的檔案或設定
- 切換期間的資料可能還在舊主機
- 出問題時可以快速切回
如果網址也要改變
那是另一個層級的工作,需要完整的轉址對應。見網站改版或更換網域,SEO 排名怎麼保住。
建議不要同時換主機與換網址——出問題時難以判斷原因。
常見的搬遷災難
- 測試站的搜尋引擎封鎖設定被帶到正式環境——網站正常但搜尋不到,可能數月後才發現
- 排程工作沒有設定——自動作業靜默停止
- 郵件設定不完整——通知信全部進垃圾桶
- 切換期間的訂單遺失
- 憑證未先安裝——切換後出現安全警告
第一項最嚴重也最常見,務必列入切換後的檢查。
各主機商的方案內容、規格定義與限制條件差異很大,本文說明的是評估的原則與該確認的項目。實際規格與條款請以各服務商的現行公告為準。
VPS 的真實成本包含時間
VPS 的月費看起來可能與虛擬主機差不多,但它多了一整套管理責任。
這些工作不做不會立刻出事,但長期累積的風險很高:系統沒更新、備份沒建立、故障時沒人能處理。
評估時應該問:這些工作誰來做?時間成本算進去之後,還划算嗎?
自管 VPS 需要的能力
基本必備
- 命令列的基本操作
- 檔案權限與使用者管理
- 套件的安裝與更新
- 服務的啟動、停止、查看狀態
- 日誌的位置與判讀
進階需要
- 網頁伺服器的設定
- 資料庫的安裝與調校
- 防火牆與存取控制
- 備份機制的建立與驗證
- 故障時的排查能力
如果這些都要現學,建議先評估代管型方案——把學習與試錯的時間拿去做本業,通常更有價值。
取得 VPS 後的初始設定清單
這是最容易被省略、卻最重要的階段。
一、存取安全
- 建立一般使用者帳號,不要直接用最高權限帳號日常操作
- 改用金鑰登入,停用密碼登入
- 停用最高權限帳號的直接遠端登入
- 變更預設的遠端連接埠——這不是真正的安全機制,但能減少自動化掃描
二、防火牆
只開放需要的連接埠:
- 網頁服務所需的連接埠
- 遠端管理的連接埠——建議限制來源位址
- 資料庫連接埠不對外開放
三、系統更新
- 執行一次完整更新
- 設定安全性更新的自動安裝
- 建立定期檢視更新的習慣
四、時區與語系
確認系統時區正確,否則日誌時間、排程執行、資料庫記錄的時間都會錯亂。
五、監控與通知
- 磁碟用量
- 記憶體與負載
- 服務是否正常運作
- 設定異常時的通知
六、備份
在放上任何正式資料之前就建立好備份機制。
要包含:網站檔案、資料庫、設定檔。並存到主機以外的位置。
最常被忽略的三件事
一、交換空間
記憶體較小的 VPS 若沒有設定交換空間,記憶體用盡時行程會被系統直接終止——症狀是服務莫名其妙停止。
設定適量的交換空間可以緩衝,但它不能替代足夠的記憶體。
二、日誌輪替
日誌不輪替會持續累積,最終塞滿磁碟導致所有服務停止。
這是實際會發生的事故,而且症狀看起來像是「網站突然壞了」。
三、資源限制的相互關聯
程式語言的行程數、資料庫的連線數、記憶體上限,這幾個設定要互相對應。
設定不協調時,流量稍高就會出現各種奇怪的錯誤。 見網頁伺服器的效能相關設定。
快照與備份不是同一件事
| 快照 | 備份 | |
|---|---|---|
| 內容 | 整台機器的狀態 | 資料與檔案 |
| 還原 | 回到某個時間點的整機狀態 | 可選擇性還原 |
| 存放位置 | 通常在同一個服務商 | 應存到別處 |
| 適合 | 系統變更前的保險 | 資料的長期保存 |
快照方便但不能取代備份——若服務商端出問題,快照可能一起受影響。
代管與自管的比較
| 自管 VPS | 代管型 | |
|---|---|---|
| 月費 | 較低 | 較高 |
| 系統更新 | 自己 | 服務商 |
| 故障排除 | 自己 | 服務商協助 |
| 備份 | 自己建立 | 通常包含 |
| 控制權 | 完整 | 視方案 |
| 適合 | 有技術人力 | 需要獨立資源但無人力 |
怎麼判斷
估算一下:每個月花在系統管理上的時間 × 你的時薪,加上月費。
再與代管型的月費比較。對多數中小企業,代管型通常比較划算——而且出事時有人可以求助。
把責任寫進合約
如果由網頁設計公司或維護廠商協助管理,應明確約定:
- 系統更新由誰負責、多久一次
- 備份由誰執行、保留多久、存在哪裡
- 故障時的回應時間
- 監控的範圍
- 主機帳號的歸屬——應登記在業主名下
最後一項與網域註冊人是同樣的道理——管理權可以外包,所有權應該在自己手上。
什麼情況真的需要 VPS
- 需要安裝虛擬主機不支援的元件
- 需要特定的系統設定
- 資源需求超過虛擬主機的配額
- 需要多個環境——正式站與測試站分離
- 對資源穩定性有要求,不能受鄰居影響
如果只是「覺得比較專業」,那不是理由。
各主機商的方案內容、規格定義與限制條件差異很大,本文說明的是評估的原則與該確認的項目。實際規格與條款請以各服務商的現行公告為準。
先確認需求,再看方案
選虛擬主機容易被規格數字吸引,但實際會遇到的問題多半不在標示的規格上。
以下是該確認的項目,依重要性排列。
一、支援的程式語言版本
這是最容易踩雷的一項。
- 目前支援哪些版本?
- 可以自行切換嗎? 還是要開單申請
- 多久更新一次? 有些主機商長期停留在舊版
- 舊版本何時淘汰? 會提前多久通知
停留在已停止支援的版本是實際的資安風險,而且第三方套件會逐步放棄支援。見PHP 版本升級要注意什麼。
二、資料庫的限制
常見但容易忽略的限制:
- 可建立幾個資料庫
- 單一資料庫的大小上限
- 同時連線數的上限
- 是否支援遠端連線——搬遷或除錯時會用到
- 資料庫的版本
連線數上限要與網站的併發能力對應,否則流量稍高就會出現連線失敗。
三、檔案數量的限制
這是「無限空間」方案最常見的實際限制。
系統對可建立的檔案總數有上限,而網站的檔案數量成長得比想像中快——快取檔案、縮圖、日誌、郵件都算在內。
症狀是:空間明明還有很多,卻無法再上傳檔案。
四、備份政策
要問清楚四件事:
- 有沒有提供備份?
- 多久一次?保留幾份?
- 還原要怎麼申請?需要多久?
- 還原要收費嗎?
不要只依賴主機商的備份
主機商的備份通常是為了整體災難復原,不保證能還原你的單一網站到特定時間點。
建議自行建立備份機制,並保存在主機以外的地方。 備份的完整要求見操作紀錄、備份與資料救回。
五、郵件服務
- 可建立幾個信箱?各自的容量?
- 每日寄信量的上限——超過可能被暫停
- 是否支援必要的寄件驗證設定
一個實務建議
企業信箱建議與網站主機分開。 主機搬遷或出問題時,信件收發不會一起中斷。
信件寄送的問題排查見公司信箱收不到信或被當垃圾信怎麼查。
六、憑證的支援
- 是否支援免費憑證的自動申請與續期
- 自動續期真的會運作嗎——建議實際觀察第一次續期
- 是否可安裝自行購買的憑證
憑證過期會讓網站顯示明顯的安全警告,見SSL 憑證怎麼安裝與續期。
七、控制面板
面板決定了你能自己做多少事。要確認能否自行處理:
- 建立與管理資料庫
- 切換程式語言版本
- 設定排程工作
- 管理郵件帳號與轉寄
- 查看存取與錯誤日誌
- 設定轉址與網域指向
- 下載完整備份
「查看錯誤日誌」這一項很重要——沒有它,出問題時只能靠猜或開單詢問。
八、支援服務
- 服務時間——是否含假日與夜間
- 回應時間的承諾
- 聯繫方式——電話、線上客服、工單
- 技術支援的範圍——只管主機,還是也協助網站問題
價格通常不是最重要的差異——出事時找不找得到人、多久能處理,才是實際的成本。
九、續約的價格
這是常見的行銷手法:首年優惠價很低,續約時回到原價。
要確認:
- 續約價格是多少
- 是否自動續約
- 取消的條件與退費規定
年度支出的完整盤點見網站每年的固定支出有哪些。
十、機房位置
主機應該放在離主要訪客近的地方。
- 客群在台灣 → 台灣機房
- 客群在海外 → 當地或鄰近機房
要注意的是:同樣標示台灣機房,不同業者的連線品質仍有差異,這需要實測。
試用與測試的建議
正式搬遷前,可以先做這些測試:
- 建立測試站,實際安裝網站程式
- 用行動網路測試連線速度
- 測試從外部寄信到該主機的信箱
- 開一張技術支援單,測試回應速度與品質
- 下載一次完整備份,確認備份功能可用
第四項最實用——支援品質很難從網頁上判斷,實際試一次最準。
不建議只看價格的三個理由
- 過低的價格通常對應到超賣或資源限制
- 支援品質差時,你的時間成本會遠超過省下的費用
- 搬遷的成本很高——選錯了要換,工時與風險都不小
各主機商的方案內容、規格定義與限制條件差異很大,本文說明的是評估的原則與該確認的項目。實際規格與條款請以各服務商的現行公告為準。
五種常見的主機型態
| 類型 | 資源 | 管理責任 | 費用 |
|---|---|---|---|
| 虛擬主機 | 與他人共用 | 主機商負責 | 最低 |
| VPS | 獨立配額 | 多數自負 | 中 |
| 雲端主機 | 獨立且可彈性調整 | 多數自負 | 中至高,依用量 |
| 代管型主機 | 獨立 | 服務商協助管理 | 中高 |
| 實體主機 | 整台專用 | 自負 | 最高 |
最關鍵的差異不是效能,是「誰負責管理」。 這一點決定了你需要什麼能力,以及真實的總成本。
虛擬主機:多數企業網站的合理起點
一台伺服器上放多個網站,共用硬體資源,由主機商負責系統維護。
適合
- 一般企業形象網站
- 流量穩定且不大
- 沒有專職的技術人員
- 不需要特殊的系統設定
限制
- 資源與他人共用——可能受鄰居影響
- 無法安裝特定的系統元件
- 設定可調整的範圍有限
- 通常無法自行調整程式語言的細部設定
不要因為「聽說 VPS 比較快」就升級。 如果慢的原因是圖片沒壓縮,換主機也不會變快,見主機規格與網站速度的關係。
VPS:資源獨立,但要自己管
在實體伺服器上切割出獨立的虛擬環境,有自己的作業系統與資源配額。
取得的是
- 不受其他站台影響的資源配額
- 完整的系統控制權
- 可安裝任何需要的元件
同時承擔的是
- 系統更新與安全修補
- 服務的安裝與設定
- 備份機制的建立
- 故障時的排查與修復
這是最常被低估的部分。 詳見VPS 需要具備什麼能力。
雲端主機與 VPS 的差別
兩者常被混用,實務上的主要差異:
- 資源調整的彈性——雲端主機通常可即時增減規格
- 計費方式——常見按用量計費,而非固定月費
- 周邊服務——通常整合了儲存、備份、負載平衡等服務
對中小企業的實務提醒
按用量計費在流量暴增時可能產生非預期的高額費用。 若選用這類方案,建議設定用量警示與上限。
代管型主機值得考慮
介於虛擬主機與 VPS 之間:資源獨立,但系統維護由服務商負責。
適合「需要獨立資源但沒有技術人力」的情況——這其實是不少中小企業的真實處境。
費用高於自管的 VPS,但把管理的時間成本算進去,往往更划算。
規格怎麼看
處理器
虛擬主機通常不標示,VPS 會標示核心數。一般企業網站對處理器的需求不高,除非有大量運算或圖片處理。
記憶體
這是最實際的瓶頸。 網頁伺服器、程式語言的執行環境、資料庫都要用。
記憶體不足的症狀是:流量稍高就出現錯誤、資料庫連線失敗、或程式被系統終止。
儲存空間
- 類型比容量重要——固態儲存的存取速度明顯優於傳統硬碟
- 要預留成長空間——資料庫、上傳檔案、日誌、備份都會累積
流量
指的是資料傳輸量,不是訪客數。圖片多、有影片的網站消耗較快。
要確認:超過之後會怎樣——是限速、加價,還是直接停用?
「無限」通常有前提
部分方案標示無限空間或無限流量。實務上要注意:
- 通常有合理使用條款——超出「一般用途」的定義就會被限制
- 可能有檔案數量的限制——這個限制常常比空間更早到達
- 可能有資料庫大小或連線數的限制
看到「無限」時,應該去找它的實際限制寫在哪裡。
共用主機的鄰居問題
同一台上有網站流量暴增或程式耗資源,會排擠其他人。
症狀
- 速度時快時慢,沒有規律
- 某些時段特別慢
- 自己的網站沒有變動,效能卻下降
處理
先向主機商反映,多數會協助調整。若持續發生,才考慮換方案或換主機商。
資安層面的鄰居風險見主機層與存取控制的防護設定。
怎麼選:三個問題
- 有沒有人能管理系統? 沒有 → 虛擬主機或代管型
- 需要特殊的系統設定嗎? 需要 → VPS 以上
- 流量是否穩定? 波動大 → 考慮可彈性調整的方案
多數企業形象網站的答案是:虛擬主機就夠。 等到確實遇到限制再升級,而不是預先買一個用不到的規格。
各主機商的方案內容、規格定義與限制條件差異很大,本文說明的是評估的原則與該確認的項目。實際規格與條款請以各服務商的現行公告為準。
先確認可不可以抓
技術上做得到,不代表可以做。抓取他人網站的資料前,應該先確認三件事:
- 對方的使用條款是否允許
- robots.txt 的規範
- 抓取的內容是否涉及著作權或個資
幾個明確的界線
- 不要抓取個人資料——姓名、聯絡方式、帳號等,即使是公開顯示的
- 不要整批複製他人的內容用於自己的網站——這是著作權問題
- 不要繞過登入或付費機制
- 不要造成對方伺服器的負擔
著作權的相關說明見別人的內容可以用嗎。
合理的使用情境
- 抓取自己網站的資料做檢查或搬遷
- 使用對方正式提供的 API
- 取得公開資料集或有明確授權的資料
- 經對方同意的資料交換
尊重 robots.txt
from urllib.robotparser import RobotFileParser
from urllib.parse import urljoin
def can_fetch(base_url, path, agent='MyBot'):
rp = RobotFileParser()
rp.set_url(urljoin(base_url, '/robots.txt'))
try:
rp.read()
except Exception:
return False # 讀不到就保守處理
return rp.can_fetch(agent, urljoin(base_url, path))
讀不到 robots.txt 時建議保守處理,而不是預設為允許。
基本的請求設定
import requests
session = requests.Session()
session.headers.update({
'User-Agent': 'MyCompanyBot/1.0 (+https://example.com/bot)',
})
resp = session.get(url, timeout=(5, 30))
resp.raise_for_status()
三個必要的設定
- 明確的 User-Agent——包含聯絡方式或說明頁,讓對方知道是誰在抓,有問題時能聯繫
- 逾時設定——
(連線逾時, 讀取逾時)。不設逾時是常見的錯誤,對方無回應時程式會永遠卡住 raise_for_status()——讓錯誤的狀態碼拋出例外,不要靜默繼續
控制頻率
import time, random
for url in urls:
fetch(url)
time.sleep(random.uniform(1.0, 2.5))
不要用固定的極短間隔連續請求。 這既是禮貌,也是自保——過度頻繁的請求可能被封鎖,甚至被視為攻擊行為。
建議的原則:把對方的伺服器當成你自己的網站來對待。
重試機制
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
retry = Retry(
total=3,
backoff_factor=1.0,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=['GET', 'HEAD'],
)
session.mount('https://', HTTPAdapter(max_retries=retry))
重要的注意事項
- 429 代表請求過於頻繁——收到時應該放慢,而不是立刻重試
- backoff 讓每次重試的間隔逐次拉長,避免持續衝擊
- 只對讀取類的請求自動重試——寫入類的重試可能造成重複
解析 HTML
from bs4 import BeautifulSoup
soup = BeautifulSoup(resp.text, 'html.parser')
title = soup.select_one('h1')
title_text = title.get_text(strip=True) if title else ''
for a in soup.select('article a[href]'):
print(a['href'], a.get_text(strip=True))
兩個實務提醒
- 取值前先確認元素存在——網頁結構會變,直接取值容易拋出例外
- 編碼問題——若出現亂碼,可嘗試指定
resp.encoding或使用resp.apparent_encoding
內容由前端程式產生的情況
如果目標頁面的內容需要執行 JavaScript 才會出現,用一般的請求抓不到。
兩種處理方向
- 找出資料的實際來源——多數情況前端是呼叫某個 API 取得資料,直接取用該來源更穩定也更輕量
- 使用瀏覽器自動化工具——資源消耗大得多,是最後手段
優先找第一種。 用瀏覽器開發者工具的網路分頁,通常能看到實際的資料請求。
串接正式 API 的實務
def call_api(session, url, params=None):
resp = session.get(url, params=params, timeout=(5, 30))
if resp.status_code == 429:
wait = int(resp.headers.get('Retry-After', 60))
logging.warning(f'頻率限制,等待 {wait} 秒')
time.sleep(wait)
return call_api(session, url, params)
resp.raise_for_status()
return resp.json()
要注意的四件事
- 金鑰不要寫在程式碼中——用環境變數
- 注意頻率限制——查閱對方文件的規範
- 處理分頁——大量資料通常需要逐頁取得
- 記錄失敗的項目——之後可以只重試失敗的部分
串接的評估與維護見API 串接前要確認什麼。
自己網站的檢查腳本
這是最沒有爭議、也最實用的應用。
檢查連結是否失效
def check_links(urls, session):
broken = []
for u in urls:
try:
r = session.head(u, timeout=10, allow_redirects=True)
if r.status_code >= 400:
broken.append((u, r.status_code))
except Exception as e:
broken.append((u, str(e)))
time.sleep(0.5)
return broken
用 HEAD 而非 GET——只要狀態碼的話不必下載內容。但部分伺服器不支援 HEAD,失敗時可退回 GET。
其他實用的檢查
- 所有頁面是否都有標題與描述
- 圖片是否都有替代文字
- 是否有混合內容(頁面是 https 但資源是 http)
- 網站地圖中的網址是否都能正常開啟
技術檢查的項目見技術 SEO 的常見問題排查。
資料的儲存與去重
import json
from pathlib import Path
def save_progress(data, path):
tmp = Path(path).with_suffix('.tmp')
with open(tmp, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
tmp.replace(path) # 原子性寫入
長時間執行的抓取要能中斷後續跑——定期存檔進度,並記錄已處理的項目,避免重複。
四個自我約束
- 只抓需要的資料,不要整站複製
- 控制頻率,不造成對方負擔
- 標明身分,讓對方能聯繫你
- 收到停止的要求就停止
優先使用對方提供的 API 或資料集——那才是對方希望你使用的方式,也最穩定。
本文的範例以 Python 3 為例,實際的套件版本與 API 可能隨版本更新而異。在正式環境執行任何批次處理前,請先以少量資料測試並確實備份。
批次處理的價值
網站上線前常常需要處理數十到數百張圖片:縮到適當尺寸、壓縮、轉換格式、統一命名。
手動處理耗時且容易遺漏,寫一次腳本可以重複使用。
圖片規格的原則見圖片尺寸與壓縮。
基本的縮圖與壓縮
from PIL import Image
from pathlib import Path
def resize_image(src, dst, max_width=1200, quality=82):
with Image.open(src) as im:
# 依 EXIF 方向資訊自動轉正
from PIL import ImageOps
im = ImageOps.exif_transpose(im)
if im.width > max_width:
ratio = max_width / im.width
new_size = (max_width, int(im.height * ratio))
im = im.resize(new_size, Image.LANCZOS)
# JPEG 不支援透明,需先轉換
if im.mode in ('RGBA', 'P'):
im = im.convert('RGB')
im.save(dst, 'JPEG', quality=quality, optimize=True,
progressive=True)
四個重點
exif_transpose——手機拍的照片常帶有方向資訊,不處理會呈現橫倒LANCZOS——縮圖品質較好的演算法- 模式轉換——含透明度的圖存成 JPEG 會出錯
progressive=True——漸進式載入,大圖的體感較好
批次處理整個資料夾
def batch_resize(src_dir, dst_dir, max_width=1200):
src_dir, dst_dir = Path(src_dir), Path(dst_dir)
dst_dir.mkdir(parents=True, exist_ok=True)
exts = {'.jpg', '.jpeg', '.png', '.webp'}
count = 0
for f in src_dir.iterdir():
if f.suffix.lower() not in exts:
continue
try:
resize_image(f, dst_dir / f'{f.stem}.jpg', max_width)
count += 1
except Exception as e:
print(f'處理失敗 {f.name}: {e}')
print(f'完成 {count} 張')
輸出到不同的目錄,不要覆蓋原檔。 原始檔應保留,因為壓縮是不可逆的。
產生多種尺寸
網站常需要同一張圖的不同尺寸——列表縮圖、內容圖、主視覺。
SIZES = {
'thumb': 400,
'medium': 800,
'large': 1600,
}
def make_variants(src, dst_dir):
dst_dir = Path(dst_dir)
for name, width in SIZES.items():
out = dst_dir / f'{Path(src).stem}-{name}.jpg'
resize_image(src, out, max_width=width)
只縮小不放大——原圖比目標小時應保持原尺寸,放大只會變模糊又增加檔案大小。
轉換為較新的格式
def to_webp(src, dst, quality=80):
with Image.open(src) as im:
im.save(dst, 'WEBP', quality=quality, method=6)
method=6 是壓縮力度,數字越大檔案越小但處理越慢。批次處理時值得用。
關於 AVIF
壓縮率通常更好,但需要額外的套件支援,且處理速度較慢。建議先確認目標環境的支援情況。
實務建議
保留原始格式作為備援,同時產生新格式,讓網頁端依瀏覽器支援情況選擇。
檢查與報告
處理前先了解現況,往往能發現問題:
def audit(folder):
rows = []
for f in Path(folder).rglob('*'):
if f.suffix.lower() not in {'.jpg', '.jpeg', '.png', '.webp'}:
continue
try:
with Image.open(f) as im:
rows.append({
'path': str(f),
'width': im.width,
'height': im.height,
'kb': round(f.stat().st_size / 1024, 1),
})
except Exception:
rows.append({'path': str(f), 'error': '無法讀取'})
# 依檔案大小排序,找出最需要處理的
rows.sort(key=lambda r: r.get('kb', 0), reverse=True)
return rows
把結果輸出成 CSV,就能快速看出哪些圖片過大。 通常前二十名就佔了大部分的空間。
移除 EXIF 資訊
手機拍攝的照片可能包含拍攝地點的座標。放上網站前建議移除。
def strip_exif(src, dst):
with Image.open(src) as im:
from PIL import ImageOps
im = ImageOps.exif_transpose(im) # 先依方向轉正
data = list(im.getdata())
clean = Image.new(im.mode, im.size)
clean.putdata(data)
clean.save(dst)
注意順序:要先依方向資訊轉正,再移除,否則圖片會變成橫的。
為什麼重要
不動產、餐飲、居家服務等行業,若把含座標的照片上傳,等於公開了拍攝地點——可能涉及客戶的隱私。
加上浮水印
from PIL import Image
def add_watermark(src, dst, mark_path, opacity=128, margin=20):
with Image.open(src).convert('RGBA') as base:
with Image.open(mark_path).convert('RGBA') as mark:
# 浮水印寬度設為主圖的六分之一
w = base.width // 6
ratio = w / mark.width
mark = mark.resize((w, int(mark.height * ratio)))
alpha = mark.split()[3].point(lambda p: p * opacity // 255)
mark.putalpha(alpha)
pos = (base.width - mark.width - margin,
base.height - mark.height - margin)
base.alpha_composite(mark, pos)
base.convert('RGB').save(dst, 'JPEG', quality=85)
浮水印無法防止盜用,但能在被轉載時保留來源標示。
處理前的安全習慣
- 永遠輸出到新目錄,不要就地覆蓋
- 先用少量檔案測試,確認結果符合預期
- 處理前備份原始檔
- 記錄處理結果——成功幾張、失敗哪些
壓縮與縮圖都是不可逆的,原始檔一旦覆蓋就回不去了。
效能考量
大量圖片處理時,可用多行程加速:
from concurrent.futures import ProcessPoolExecutor
def batch_parallel(files, workers=4):
with ProcessPoolExecutor(max_workers=workers) as ex:
list(ex.map(process_one, files))
行程數不要超過 CPU 核心數太多——圖片處理是運算密集的工作,開太多反而互相競爭。
在正式主機上執行時要特別注意,避免影響網站的正常服務。建議在離峰時段或另一台機器處理。
本文的範例以 Python 3 為例,實際的套件版本與 API 可能隨版本更新而異。在正式環境執行任何批次處理前,請先以少量資料測試並確實備份。
自動化的三個原則
- 失敗要能被知道——靜默失敗是最糟的情況
- 可以重複執行——重跑一次不應造成問題
- 留下紀錄——出事時能追溯
第一項最常被忽略。排程腳本停止運作而沒人發現,比沒有這個腳本更危險——因為你以為它在跑。
用 logging 而不是 print
import logging
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
LOG_FILE = BASE_DIR / 'logs' / 'task.log'
LOG_FILE.parent.mkdir(exist_ok=True)
logging.basicConfig(
filename=LOG_FILE,
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
encoding='utf-8',
)
logging.info('開始執行')
logging.warning('略過 3 筆資料')
logging.error('連線失敗')
為什麼不用 print
- 排程執行時 print 的輸出可能不知去向
- 沒有時間戳記
- 無法分級
- 無法在不改程式的情況下調整詳細程度
錯誤處理與通知
import logging, sys, traceback
def main():
# 實際工作
...
if __name__ == '__main__':
try:
main()
logging.info('執行完成')
except Exception:
logging.error('執行失敗\n' + traceback.format_exc())
notify('備份腳本執行失敗,請查看日誌')
sys.exit(1)
通知的方式
- 寄送電子郵件
- 推送到通訊軟體
- 寫入監控系統
離開時要回傳非零的結束碼——排程系統與監控工具可以據此判斷失敗。
只監控失敗是不夠的
這一點很重要:如果排程根本沒執行,就不會有失敗通知。
建議的做法
- 成功時也記錄一筆,並定期確認有這筆紀錄
- 檢查產出的檔案——備份檔的時間戳是否為最新
- 設定「超過 N 小時未成功執行就通知」的監控
這與 API 串接的靜默失效是同樣的問題。
資料庫備份腳本
import subprocess, gzip, shutil, os
from pathlib import Path
from datetime import datetime, timedelta
BACKUP_DIR = Path('/backup/db')
KEEP_DAYS = 14
def backup(dbname):
BACKUP_DIR.mkdir(parents=True, exist_ok=True)
ts = datetime.now().strftime('%Y%m%d_%H%M%S')
sql_path = BACKUP_DIR / f'{dbname}_{ts}.sql'
cmd = [
'mysqldump',
'--defaults-extra-file=/etc/mysql/backup.cnf',
'--single-transaction', '--routines', '--triggers', '--events',
'--default-character-set=utf8mb4',
dbname,
]
with open(sql_path, 'wb') as out:
subprocess.run(cmd, stdout=out, check=True)
# 壓縮
gz_path = sql_path.with_suffix('.sql.gz')
with open(sql_path, 'rb') as f_in, gzip.open(gz_path, 'wb') as f_out:
shutil.copyfileobj(f_in, f_out)
sql_path.unlink()
# 驗證檔案不是空的
if gz_path.stat().st_size < 1024:
raise RuntimeError('備份檔案異常過小')
return gz_path
三個重點
- 密碼放在設定檔而非指令列——指令列參數會出現在行程列表
check=True——指令失敗時會拋出例外,不會靜默continue- 驗證產出檔案——大小異常就視為失敗
備份的完整要求見資料庫備份與還原的技術要點。
清理過期檔案
from datetime import datetime, timedelta
def cleanup(folder, keep_days=14):
cutoff = datetime.now() - timedelta(days=keep_days)
removed = 0
for f in Path(folder).glob('*.gz'):
if datetime.fromtimestamp(f.stat().st_mtime) < cutoff:
f.unlink()
removed += 1
logging.info(f'清除過期備份 {removed} 個')
務必先用「只列出不刪除」的版本確認,刪錯檔案是無法復原的。
日誌分析
import re
from collections import Counter
pattern = re.compile(r'^(\S+) .* "(\w+) (\S+).*" (\d{3})')
def analyze(log_path, top=20):
ips, paths, status = Counter(), Counter(), Counter()
with open(log_path, encoding='utf-8', errors='replace') as f:
for line in f:
m = pattern.match(line)
if not m:
continue
ip, method, path, code = m.groups()
ips[ip] += 1
status[code] += 1
if code == '404':
paths[path] += 1
return ips.most_common(top), status, paths.most_common(top)
實用的分析方向
- 請求量異常的來源——可能是掃描或攻擊
- 404 最多的路徑——改版後的遺漏轉址
- 狀態碼分布——5xx 突然增加代表有問題
日誌判讀見Nginx 與 Apache 的疑難排解。
排程設定
# crontab -e # 每日凌晨 3 點備份 0 3 * * * /path/to/.venv/bin/python /path/to/scripts/backup.py # 每小時檢查 0 * * * * /path/to/.venv/bin/python /path/to/scripts/check.py
四個常見的排程陷阱
- 沒用絕對路徑——排程的工作目錄與環境變數與登入時不同
- 沒指定虛擬環境的直譯器——會用到系統的 Python 而找不到套件
- 時區設定不同——確認伺服器時區
- 執行時間重疊——上一次還沒跑完就啟動下一次
防止重複執行
import fcntl, sys
lock_file = open('/tmp/mytask.lock', 'w')
try:
fcntl.flock(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
except BlockingIOError:
logging.warning('前一次執行尚未結束,本次略過')
sys.exit(0)
可重複執行的設計
腳本應該重跑一次也不會造成問題:
- 寫入前先檢查是否已存在
- 用「更新或新增」而非單純新增
- 檔案輸出時包含時間戳,或先寫暫存檔再改名
# 先寫暫存檔,完成後才改名
tmp = target.with_suffix('.tmp')
write_data(tmp)
tmp.replace(target) # 原子性操作
這樣即使中途失敗,也不會留下半完成的檔案。
先做不會造成傷害的版本
任何會刪除、覆蓋、修改資料的腳本,建議都先做一個「只顯示會做什麼」的模式:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--dry-run', action='store_true',
help='只顯示會執行的動作,不實際執行')
args = parser.parse_args()
if args.dry_run:
print(f'[模擬] 將刪除 {f}')
else:
f.unlink()
這個習慣可以避免大部分的災難。
本文的範例以 Python 3 為例,實際的套件版本與 API 可能隨版本更新而異。在正式環境執行任何批次處理前,請先以少量資料測試並確實備份。
中文 CSV 的編碼問題
這是實務上最常遇到的坑。同一個檔案,用不同軟體開啟可能出現亂碼。
問題的來源
- Excel 在部分環境下預設用系統編碼開啟 CSV,而非 UTF-8
- 舊系統可能匯出 Big5 編碼
- 不同來源的檔案編碼不一致
讓 Excel 正確開啟的做法
在 UTF-8 檔案開頭加上位元組順序記號,Excel 就能正確辨識:
import csv
with open('output.csv', 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f)
writer.writerow(['標題', '內容', '日期'])
writer.writerow(['測試', '中文內容', '2026-08-05'])
關鍵是 encoding='utf-8-sig'——它會自動加上記號。
兩個必要的參數
newline=''——不加的話在某些系統會產生多餘的空行encoding——明確指定,不要依賴系統預設
讀取來源不明的檔案
def read_csv_auto(path):
encodings = ['utf-8-sig', 'utf-8', 'cp950', 'big5']
for enc in encodings:
try:
with open(path, newline='', encoding=enc) as f:
return list(csv.DictReader(f)), enc
except UnicodeDecodeError:
continue
raise ValueError('無法判斷編碼')
注意 cp950 要放在 big5 前面——它是 Big5 的擴充,涵蓋較多字元。
Big5 的字元缺漏
若需要輸出 Big5 給舊系統,部分字元可能無法轉換——例如某些罕用字、特殊符號、以及使用者造字。
text = '測試內容'
try:
data = text.encode('cp950')
except UnicodeEncodeError as e:
print(f'無法轉換的字元位置: {e.start}')
# 可改用替代字元或 HTML 實體
data = text.encode('cp950', errors='xmlcharrefreplace')
實務建議:轉換前先檢查哪些字元會有問題,決定是替換、改用實體標記,還是回報給來源修正。
用 DictReader 與 DictWriter
處理有標題列的 CSV 時,用字典的方式比索引可靠得多:
import csv
# 讀取
with open('input.csv', newline='', encoding='utf-8-sig') as f:
rows = list(csv.DictReader(f))
for row in rows:
print(row['title'], row['slug'])
# 寫出
fields = ['title', 'slug', 'content']
with open('output.csv', 'w', newline='', encoding='utf-8-sig') as f:
w = csv.DictWriter(f, fieldnames=fields)
w.writeheader()
w.writerows(rows)
好處是欄位順序改變時程式不會壞掉,也比 row[3] 這種寫法容易理解。
欄位中包含換行或逗號
CSV 模組會自動處理引號跳脫,不要自己用字串拼接產生 CSV。
# 不要這樣做
line = f'{title},{content}\n' # content 有逗號就爆了
# 應該用 csv 模組
writer.writerow([title, content])
內容含 HTML 或多行文字時,這一點特別重要。
Excel 檔案的處理
from openpyxl import load_workbook, Workbook
# 讀取
wb = load_workbook('data.xlsx', data_only=True)
ws = wb.active
for row in ws.iter_rows(min_row=2, values_only=True):
print(row)
# 寫出
wb = Workbook()
ws = wb.active
ws.append(['標題', '數量'])
ws.append(['測試', 100])
wb.save('output.xlsx')
data_only=True 會讀取公式的計算結果而非公式本身——多數情況這才是你要的。
注意事項
- 舊的
.xls格式需要不同的套件 - 大型檔案讀取時考慮使用唯讀模式以節省記憶體
- 儲存格格式與樣式需要另外處理
JSON 處理
import json
# 讀取
with open('data.json', encoding='utf-8') as f:
data = json.load(f)
# 寫出(中文不要被轉成跳脫序列)
with open('output.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
ensure_ascii=False 很重要——不加的話中文會變成一堆跳脫字元,雖然功能正常但完全無法閱讀。
批次重新命名檔案
from pathlib import Path
import re
folder = Path('/path/to/images')
for f in folder.glob('*.jpg'):
# 轉小寫、空格改連字號、移除特殊字元
new_name = re.sub(r'[^a-z0-9\-.]', '',
f.name.lower().replace(' ', '-'))
if new_name != f.name:
target = f.with_name(new_name)
if target.exists():
print(f'略過(已存在): {new_name}')
continue
f.rename(target)
print(f'{f.name} -> {new_name}')
三個安全習慣
- 先跑一次「只印出不執行」的版本,確認結果符合預期
- 檢查目標檔名是否已存在,避免覆蓋
- 處理前備份
檔名對搜尋的影響見圖片的替代文字與檔名該怎麼寫。
資料清理的常見需求
import re
def clean_text(s):
if s is None:
return ''
s = str(s)
s = s.replace('\u00a0', ' ') # 不斷行空白
s = re.sub(r'[ \t]+', ' ', s) # 連續空白
s = re.sub(r'\n{3,}', '\n\n', s) # 過多空行
return s.strip()
不斷行空白是最常見的隱形問題——它看起來像空格但不是,會造成比對失敗或版面異常。
處理大檔案
不要一次把整個檔案載入記憶體:
with open('large.csv', newline='', encoding='utf-8-sig') as f:
for row in csv.DictReader(f):
process(row) # 逐列處理
需要輸出時同樣逐列寫入,記憶體用量就與檔案大小無關。
本文的範例以 Python 3 為例,實際的套件版本與 API 可能隨版本更新而異。在正式環境執行任何批次處理前,請先以少量資料測試並確實備份。
不要動系統內建的 Python
多數 Linux 發行版內建的 Python 是系統工具依賴的執行環境。直接用它安裝套件、或升級版本,可能導致系統管理工具失效。
原則:系統的 Python 用來跑系統的東西,你的專案用自己的環境。
虛擬環境是基本做法
虛擬環境讓每個專案有獨立的套件目錄,彼此不干擾。
# 建立 python3 -m venv .venv # 啟用(Linux / macOS) source .venv/bin/activate # 啟用(Windows) .venv\Scripts\activate # 離開 deactivate
為什麼一定要用
- 不同專案可能需要不同版本的套件
- 不會污染系統環境
- 可以完整記錄專案的相依套件
- 刪除整個目錄就等於清除環境,不留殘留
建議把 .venv 加入版本控制的忽略清單——它應該由設定檔重建,而不是直接納入版控。
套件管理
記錄相依套件
pip freeze > requirements.txt
在另一台機器重建
python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt
一個實務建議
pip freeze 會列出所有套件(含相依的相依),版本鎖得很死。
比較好的做法是手動維護一份只列出直接使用的套件,例如:
requests>=2.31 Pillow>=10.0 openpyxl>=3.1
需要完全重現時再另外產生鎖定版本的檔案。
版本的選擇
Python 每個版本大約有五年的支援期。正式使用的腳本建議選擇仍在支援期內的版本,並避免使用剛發布的最新版——部分套件可能還沒跟上。
實務上,選擇比最新版舊一到兩個小版本,通常是穩定與相容性的平衡點。
確認版本
python3 --version pip --version pip list
同一台機器有多個版本
可以並存,用明確的版本號呼叫:
python3.11 -m venv .venv311 python3.12 -m venv .venv312
不要修改系統的 python3 指向,那會影響系統工具。
專案的目錄結構
簡單的自動化腳本專案,建議這樣安排:
project/ ├── .venv/ # 虛擬環境(不納入版控) ├── scripts/ # 執行的腳本 ├── lib/ # 共用的函式 ├── config/ # 設定檔(敏感資訊不納入版控) ├── logs/ # 執行紀錄 ├── output/ # 產出檔案 ├── requirements.txt └── README.md
README 要寫清楚怎麼建置環境與執行——三個月後的自己會感謝你。
敏感資訊不要寫在程式碼裡
資料庫密碼、API 金鑰、主機帳號不應該直接寫在腳本中。
常見做法
import os
DB_PASSWORD = os.environ.get('DB_PASSWORD')
API_KEY = os.environ.get('API_KEY')
if not DB_PASSWORD:
raise SystemExit('缺少必要的環境變數 DB_PASSWORD')
或使用獨立的設定檔,並確實加入版本控制的忽略清單。
為什麼重要
- 避免密碼隨程式碼進入版本控制
- 不同環境可用不同設定
- 指令列中的密碼會出現在行程列表,可被同機的其他使用者看到
在伺服器上執行的注意事項
用絕對路徑
排程執行時的工作目錄可能與你預期的不同。腳本中的檔案路徑建議用絕對路徑,或明確設定工作目錄。
from pathlib import Path BASE_DIR = Path(__file__).resolve().parent LOG_FILE = BASE_DIR / 'logs' / 'run.log'
指定虛擬環境的直譯器
排程中不會自動啟用虛擬環境,要直接指定:
/path/to/project/.venv/bin/python /path/to/project/scripts/backup.py
權限
執行的身分要有讀寫相關目錄的權限。不要為了方便而用最高權限執行——腳本出錯時的破壞範圍會大很多。
常用的標準函式庫
很多需求不需要額外安裝套件:
| 模組 | 用途 |
|---|---|
pathlib | 檔案路徑處理,比字串拼接安全 |
csv | CSV 讀寫 |
json | JSON 處理 |
logging | 執行紀錄 |
argparse | 命令列參數 |
subprocess | 執行外部指令 |
datetime | 日期時間 |
shutil | 檔案複製與壓縮 |
能用標準函式庫解決的,就不要多裝套件——減少相依就減少維護負擔。
本文的範例以 Python 3 為例,實際的套件版本與 API 可能隨版本更新而異。在正式環境執行任何批次處理前,請先以少量資料測試並確實備份。
準備好讓網站 開始幫你帶生意了嗎?
不論是要做新網站、救舊網站,還是只想先聊聊方向——先諮詢,不用先付錢,我們照實給你建議。