跳至主要内容

用 Python 做網站?

網站是如何運作的

用 Python 做網站?

Python 是一款通用性的程式語言,可以用來做很多事情,例如資料分析、機器學習,或是寫爬蟲機器人幫我們搶張學友演唱會門票...等等都可以,其中也包括可以用來做網站。如果要拿 Python 做網站的話,大家可能會想到一些比較流行的網站開發框架,例如 Flask、FastAPI 或 Django 之類的,但其實就算不用任何框架或套件,用 Python 本身內建的模組就做到了。在 Python 不管是選擇哪一種網站開發框架,應該或多或少會看到一個英文縮寫:WSGI。

WSGI

WSGI 的全名是「Web Server Gateway Interface」,從字面上大概可以猜出這是一種介面、規範或協議,詳細的規格可翻閱 PEP 3333 說明,最主要是定義網站伺服器與 Python 應用程式之間的應該怎麼溝通。有這個協議之後,只要網站伺服器支援 WSGI,Python 寫的應用程式就可以在這些不同牌子的網站伺服器上運行,例如 Apache、Nginx 等。

如果還是覺得有點抽像的話,我再舉個例子。假設你的手機有支援無線充電,只要有支援無線充電規格的充電座,不管是原廠還是副廠甚至是不同品牌也沒關係,只要把手機放上去就可以進行充電了。在這裡,手機就是 Python 應用程式,充電座就是網站伺服器,而無線充電的規格就是 WSGI。

如果把網站伺服器、WSGI 伺服器以及 Python 應用程式之間的關係串起來,可能會像這樣:

WSGI

有些 WSGI 伺服器本身也能充當網站伺服器,中間不需要多墊一層網站伺服器:

WSGI

或是也可能在 WSGI 伺服器前面再加一層反向代理伺服器(Reverse Proxy)或負載平衡器(Load Balancer),用來分散流量,讓網站運作更順暢:

WSGI

這都是常見的架構。

除了 WSGI 外,在後面章節介紹講到 Django 的時候也可能還會看到另一個跟 WSGI 有點像的縮寫叫做 ASGI。ASGI 是「Asynchronous Server Gateway Interface」的縮寫,跟 WSGI 一樣也是一種介面,基本上跟 WSGI 的角色差不多,差別在於 ASGI 的 A 指的是非同步的意思。

知道 WSGI 的角色跟運作流程後,我們來試試看怎麼用它寫一個簡單的網站。首先,先隨便新增一個檔案,例如我就叫它 kitty.py,然後把下面的程式碼貼上去:

from wsgiref.simple_server import make_server

def hello(env, response):
response("200 OK", [("Content-type", "text/html")])
return [b"<h1>Hello Kitty!</h1>"]

if __name__ == "__main__":
port = 9527
web_server = make_server(host="", port=port, app=hello)

print(f"Serving on port { port }...")
web_server.serve_forever()

首先,我從 wsgiref.simple_server 模組中引入 make_server() 函數,待會準備用它來建立一個簡單的 WSGI 伺服器。接著我隨便定義了一個 hello() 函數,光看這個函數的命名就知道真的很隨便了。hello() 函數接收兩個參數,第一個是環境變數,包括瀏覽器是哪個牌子、連線的 IP 位置等資訊都會在這個參數裡,待會會用到它。第二個參數是一個函數,這是待會網站伺服器用來回應瀏覽器的請求的時候會執行的函數。

因為這個 hello() 是待會我會用來掛在 WSGI 伺服器上的應用程式,所以這個函數的規格必須要符合 WSGI 的規範。根據 WSGI 的規範,這個函數的回傳值必須要是一個「可迭代物件」,而且裡面的元素是位元組(Bytes),所以最後我準備回傳一個串列,裡面先直接放了一個位元組。為什麼不是字串而是位元組?大家可能不知道其實透過 HTTP 傳送資料的時候,基本上都是在傳送位元組(Bytes),即使是字串或數字都會先被轉換成位元組再傳送。

hello() 函數執行的過程中,還需要準備一些額外的資訊給瀏覽器,例如狀態碼是 200 OK 以及回應的內容類型是 HTML,這會在回應 HTTP 請求的時候透過傳進 hello() 函數的第二個參數的那個函數來設定。

make_server() 這個函數的名字很容易猜出它的用途,就是用來建立 WSGI 伺服器。這裡我把 hello() 函數傳進去並且打算把伺服器開在 Port 9527,這樣待會伺服器啟動的時候,就知道要用 hello() 函數來處理 HTTP 請求了。

最後執行 .serve_forever() 方法啟動,這樣伺服器就會一直運行,直到你手動停止它。

試著執行看看:

$ python kitty.py
Serving on port 9527...

打開瀏覽器,應該就會在 http://localhost:9527 看到 Hello Kitty! 字樣,這就是透過 WSGI 介面做的超陽春網站。

目前這樣的寫法,整個網站都只能回應固定的內容,如果想要回應不同的內容,就需要透過剛剛提到的環境變數來做一些判斷:

def hello(env, response):
response("200 OK", [("Content-type", "text/html")])

path = env["PATH_INFO"]
if path == "/meow":
return [b"<h1>Meow</h1>"]

return [b"<h1>Hello Kitty!</h1>"]

我在中間加上了路徑判斷,如果網址是 /meow 的話就會看到 Meow 字樣,其他的網址都會看到 Hello Kitty! 字樣。提醒一下,每次改完程式碼之後需要重新執行程式才會生效喔。

這就是用 Python 寫網站的手感,感覺怎麼樣?應該不會覺得很好寫吧,但這就是最基礎的 WSGI 寫起來的樣子。不得不說,這樣的寫法有點囉嗦、原始,還好這個世界上有很多厲害又樂於分享的善心人士,幫我們這些凡人做了很多好用的套件或框架讓我們不用處理這些瑣事,像是接下來要介紹的 Flask 以及之後的 Django,它們都是建構在 WSGI 的基礎上,讓我們可以把重點放在網站的功能,而不是一直花時間在這些細節上。

如果 Flask 跟 Django 都是一種基於 WSGI 的框架,那它們之間的差別是什麼?Flask 是一個輕量級的框架,本身的功能雖然少,但週邊支援的套件很多,要做出功能完善的網站是沒問題的。相對比較之下,Django 則是一個功能較完整的框架,把一些網站開發會用到的套件都預先挑好,所以如果你是新手,或是想要快速開發一個網站,Django 可能是個不錯的選擇;如果你是比較有經驗的開發者,或是想要有更多的自由度,Flask 可能會比較適合你。

接下來,就讓我們先來試試 Flask 的手感吧!

工商服務

想學 Python 嗎?我教你啊 :)

想要成為軟體工程師嗎?這不是條輕鬆的路,除了興趣之外,還需要足夠的決心、設定目標並持續學習,我們的ASTROCamp 軟體工程師培訓營提供專業的前後端課程培訓,幫助你在最短時間內建立正確且扎實的軟體開發技能,有興趣而且不怕吃苦的話不妨來試試看!