# 練習50.你的第一個網站
最后的3個練習將會很難,你需要在他們身上多花一些時間。第一個練習里你將創建一個簡單的web版本的游戲。在你開始這節練習以前,你必須已經成功地完成過了《習題 46》的內容,正確安裝了pip,而且學會了如何安裝軟件包以及如何創建項目骨架。如果你不記得這些內容,就回到《習題 46》重新復習一遍。
## 安裝 lpthw.web
在創建你的第一個網頁應用程序之前,你需要安裝一個“Web 框架”,它的名字叫`lpthw.web`。所謂的“框架”通常是指“讓某件事情做起來更容易的軟件包”。在網頁應用的世界里,人們創建了各種各樣的“網頁框架”,用來解決他們在創建網站時碰到的問題,然后把這些解決方案用軟件包的方式發布出來,這樣你就可以利用它們引導創建你自己的項目了。
可選的框架類型有很多很多,不過在這里我們將使用`lpthw.web` 框架。你可以先學會它,等到差不多的時候再去接觸其它的框架,不過 `lpthw.web` 本身挺不錯的,所以就算你一直使用也沒關系。
使用`pip`安裝`lpthw.web`:
~~~
$ sudo pip install lpthw.web
[sudo] password for zedshaw:
Downloading/unpacking lpthw.web
Running setup.py egg_info for package lpthw.web
Installing collected packages: lpthw.web
Running setup.py install for lpthw.web
Successfully installed lpthw.web
Cleaning up...
~~~
以上是 Linux 和 Mac OSX 系統下的安裝命令,如果你使用的是 Windows,那你只要把`sudo`去掉就可以了。如果你無法正常安裝,請回到《習題 46》,確認自己學會了里邊的內容。
> **Warning:**其他 Python 程序員會警告你說`lpthw.web`只是另外一個叫做`web.py`的 Web 框架的代碼分支(fork),而`web.py`又包含了太多的“魔法(magic)”在里邊。如果他們這么說的話,你告訴他們`Google App Engine`最早用的就是 `web.py`,但沒有一個 Python 程序員抱怨過它里邊包含了太多的魔法,因為 Google 用它也沒啥問題。如果Google覺得它可以,那它對你來說也不會差。所以還是回去繼續學習吧,他們這些說法與其說是教導你,不如說是拿他們自己的教條束縛你,你還是忽略這些說法好了。
## 寫一個簡單的“Hello World”項目
現在我們使用`lpthw.web`做一個非常簡單的“Hello World”項目出來,首先你要創建一個項目目錄:
~~~
$ cd projects
$ mkdir gothonweb
$ cd gothonweb
$ mkdir bin gothonweb tests docs templates
$ touch gothonweb/__init__.py
$ touch tests/__init__.py
~~~
你最終的目的是把《習題43》中的游戲做成一個web應用,所以你的項目名稱叫做`gothonweb`,不過在此之前,你需要創建一個最基本的 `lpthw.web` 應用,將下面的代碼放到 `bin/app.py` 中:
~~~
import web
urls = (
'/', 'index'
)
app = web.application(urls, globals())
class index:
def GET(self):
greeting = "Hello World"
return greeting
if __name__ == "__main__":
app.run()
~~~
然后使用下面的方法來運行這個 web 程序:
~~~
$ python bin/app.py
http://0.0.0.0:8080/
~~~
如果你這樣做:
~~~
$ cd bin/ # WRONG! WRONG! WRONG!
$ python app.py # WRONG! WRONG! WRONG!
~~~
那么你就錯了。在所有Python項目中,都不會用`cd`到下一級目錄中去啟動服務,你就在最頂層的目錄啟動服務,這樣所有的系統可以訪問所有的模塊和文件。 去重讀習題46并理解項目布局和如何使用它。
最后,使用你的網頁瀏覽器,打開 URL `http://localhost:8080/`,你應該看到兩樣東西,首先是瀏覽器里顯示了 `Hello, world!`,然后是你的命令行終端顯示了如下的輸出:
~~~
$ python bin/app.py
http://0.0.0.0:8080/
127.0.0.1:59542 - - [13/Jun/2011 11:44:43] "HTTP/1.1 GET /" - 200 OK
127.0.0.1:59542 - - [13/Jun/2011 11:44:43] "HTTP/1.1 GET /favicon.ico" - 404 Not Found
~~~
這些是`lpthw.web` 打印出的 log 信息,從這些信息你可以看出服務器有在運行,而且能了解到程序在瀏覽器背后做了些什么事情。這些信息還有助于你發現程序的問題。例如在最后一行它告訴你瀏覽器試圖獲取`/favicon.ico`,但是這個文件并不存在,因此它返回的狀態碼是 `404 Not Found`。
到這里,我還沒有講到任何 web 相關的工作原理,因為首先你需要完成準備工作,以便后面的學習能順利進行,接下來的兩節習題中會有詳細的解釋。我會要求你用各種方法把你的 lpthw.web 應用程序弄壞,然后再將其重新構建起來:這樣做的目的是讓你明白運行lpthw.web 程序需要準備好哪些東西.
## 發生了什么?
在瀏覽器訪問到你的網頁應用程序時,發生了下面一些事情:
> 1. 瀏覽器通過網絡連接到你自己的電腦,它的名字叫做`localhost`,這是一個標準稱謂,表示的誰就是網絡中你自己的這臺計算機,不管它實際名字是什么,你都可以使用localhost 來訪問。它使用到的網絡端口是8080。
> 1. 連接成功以后,瀏覽器對`bin/app.py`這個應用程序發出了 HTTP 請求(request),要求訪問 URL `/`,這通常是一個網站的第一個 URL。
> 1. 在`bin/app.py` 里,我們有一個列表,里邊包含了 URL 和類的匹配關系。我們這里只定義了一組匹配,那就是 `'/', 'index'` 的匹配。它的含義是:如果有人使用瀏覽器訪問 / 這一級目錄,`lpthw.web` 將找到并加載 `class index`,從而用它處理這個瀏覽器請求。
> 1. 現在 `lpthw.web` 找到了`class index`,然后針對這個類的一個實例調用了 `index.GET` 這個方法函數。該函數運行后返回了一個字符串,以供`lpthw.web` 將其傳遞給瀏覽器。
> 1. 最后`lpthw.web` 完成了對于瀏覽器請求的處理,將響應(response)回傳給瀏覽器,于是你就看到了現在的頁面。
確定你真的弄懂了這些,你需要畫一個流程圖,來理清信息是如何從瀏覽器傳遞到`lpthw.web`,再到`index.GET`,再回到你的瀏覽器的。
## 修正錯誤
第一步,把第 11 行的`greeting`變量賦值刪掉,然后刷新瀏覽器。你應該會看到一個錯誤頁面,你可以通過這一頁豐富的錯誤信息看出你的程序崩潰的原因是什么。當然你已經知道出錯的原因是`greeting`的賦值丟失了,不過 `lpthw.web`還是會給你一個挺好的錯誤頁面,讓你能找到出錯的具體位置。試試在這個錯誤頁面上做以下操作:
> 1. 檢查每一段`Local vars`輸出(用鼠標點擊它們),追蹤里邊提到的變量名稱,以及它們是在哪些代碼文件中用到的。
> 1. 閱讀`Request Information` 一節,看看里邊哪些知識是你已經熟悉了的。Request 是瀏覽器發給你的 `gothonweb`應用程序的信息。這些知識對于日常網頁瀏覽沒有什么用處,但現在你要學會這些東西,以便寫出 web 應用程序來。3.試著把這個小程序的別的位置改錯,探索一下會發生什么事情。`lpthw.web` 的會把一些錯誤信息和堆棧跟蹤(stack trace)信息顯示在命令行終端,所以別忘了檢查命令行終端的信息輸出。
## 創建基本的模板文件
你已經試過用各種方法把這個 lpthw.web 程序改錯,不過你有沒有注意到“Hello World”不是一個好 HTML 網頁呢?這是一個 web 應用,所以需要一個合適的 HTML 響應頁面才對。為了達到這個目的,下一步你要做的是將“Hello World”以較大的綠色字體顯示出來。
第一步是創建一個`templates/index.html`文件,內容如下:
~~~
$def with (greeting)
<html>
<head>
<title>Gothons Of Planet Percal #25</title>
</head>
<body>
$if greeting:
I just wanted to say <em style="color: green; font-size: 2em;">$greeting</em>.
$else:
<em>Hello</em>, world!
</body>
</html>
~~~
如果你學過 HTML 的話,這些內容你看上去應該很熟悉。如果你沒學過 HTML,那你應該去研究一下,試著用 HTML 寫幾個網頁,從而知道它的工作原理。不過我們這里的 HTML 文件其實是一個“模板(template)”,如果你向模板提供一些參數,`lpthw.web` 就會在模板中找到對應的位置,將參數的內容填充到模板中。例如每一個出現 `$greeting`的位置,`$greeting`的內容都會被替換成對應這個變量名的參數。
為了讓你的`bin/app.py`處理模板,你需要寫一寫代碼,告訴`lpthw.web` 到哪里去找到模板進行加載,以及如何渲染(render)這個模板,按下面的方式修改你的 app.py:
~~~
import web
urls = (
'/', 'Index'
)
app = web.application(urls, globals())
render = web.template.render('templates/')
class Index(object):
def GET(self):
greeting = "Hello World"
return render.index(greeting = greeting)
if __name__ == "__main__":
app.run()
~~~
特別注意一下`render`這個新變量名,注意我修改了`index.GET` 的最后一行,讓它返回了`render.index()`,并且將`greeting` 變量作為參數傳遞給了這個函數。
改好上面的代碼后,刷新一下瀏覽器中的網頁,你應該會看到一條和之前不同的綠色信息輸出。你還可以在瀏覽器中通過“查看源文件(View Source)”看到模板被渲染成了標準有效的 HTML 源代碼。
這么講也許有些太快了,我來詳細解釋一下模板的工作原理吧:
> 1. 在`bin/app.py`里面你添加了一個叫做`render`的新變量,它本身是一個 `web.template.render`對象。
> 1. 你將`templates/` 作為參數傳遞給了這個對象,這樣就讓`render` 知道了從哪里去加載模板文件。
> 1. 在你后面的代碼中,當瀏覽器一如既往地觸發了`index.GET` 以后,它沒有再返回簡單的`greeting`字符串,取而代之的是你調用了 `render.index`,而且將問候語句作為一個變量傳遞給它。
> 1. 這個`render_template`函數可以說是一個“魔法函數”,它看到了你需要的是`index.html`,于是就跑到`templates/`目錄下,找到名字為`index.html` 的文件,然后就把它渲染(render)一遍(叫“轉換一遍”也可以)。
> 1. 在`templates/index.html`文件中,你可以看到初始定義一行中說這個模板需要使用一個叫`greeting`的參數,這和函數定義中的格式差不多。另外和 Python 語法一樣,模板文件是縮進敏感的,所以要確認自己弄對了縮進。
> 1. 最后,你讓`templates/index.html`去檢查`greeting`這個變量,如果這個變量存在的話,就打印出變量的內容,如果不存在的話,就會打印出一個默認的問候信息。
要深入理解這個過程,你可以修改 greeting 變量以及 HTML 模板的內容,看看會有什么效果。然后創建一個叫做`templates/foo.html` 的模板,并且使用一個新的`render.foo()`去渲染它。從這個過程你也可以看出, `render` 調用的函數名稱只要跟 `templates/`下的 `.html` 文件名匹配到,這個 HTML 模板就可以被渲染到了。
## 附加題
> 1. 閱讀`http://webpy.org/` 里邊的文檔,它其實和`lpthw.web` 是同一個項目。
> 1. 實驗一下你在上述網站看到的所有的東西,包括里邊的代碼示例。
> 1. 閱讀以下 HTML5 和 CSS3 相關的東西,自己練習著寫幾個 `.html` 和 `.css 文件`。
> 1. 如果你有一個懂 Django 朋友可以幫你的話,你可以試著使用 Django 完成一下習題 50、51、52,看看結果會是什么樣子的。
## 常見問題
### Q: 我好想無法連接到`http://localhost:8080/`
> 那么試試訪問`http://127.0.0.1:8080/`
### Q: `lpthw.web` 和 `web.py`有什么區別?
> 沒有區別。我只是在特定版本“鎖定”`web.py`,以使它對所有學生都是一樣的,然后再命名為`lpthw.web`,上一個版本的`web.py`可能就不同于這一版本。
### Q: 我的代碼找不到`index.html`(或者其他文件)
> 你可能是先執行了`cd bin/`,不要執行這一句,所有的命令都應該在`bin/`的上一級目錄執行,所以如果你不能執行`python bin/app.py`,說明你在錯誤的目錄上。
### Q: 當我們調用模板的時候,為什么要執行`greeting=greeting`賦值操作
> 你并沒有給`greeting`賦值,你只是給模板設定一個命名參數。這是聲明的一種,但它只影響調用模板的功能。
### Q: 我的電腦上不能使用8080端口
> 你可能有一個殺毒程序占用了這個端口,試試別的端口。
### Q: 安裝`lpthw.web` 時,我遇到報錯信息`ImportError "No module named web"`
> 你可能安裝了多個版本的Python并且正在使用一個錯誤的版本,或者你是因為使用了一個舊版本的`pip`,導致安裝沒有成功,試著先卸載`lpthw.web`,在重裝一次,如果還沒有解決問題,再次確認下你是否使用了正確的Python版本。
- 序言
- 前言
- 簡介
- 0:安裝和準備
- 1:第一個程序
- 2:注釋和“#”井號
- 3:數字和數學計算
- 4:變量和命名
- 5:更多的變量和打印
- 6:字符串和文本
- 7:更多的打印(輸出)
- 8:打印, 打印
- 9:打印, 打印, 打印
- 10:那是什么?
- 11:提問
- 12:提示別人
- 13:參數, 解包, 變量
- 14:提示和傳遞
- 15:讀文件
- 16:讀寫文件
- 17:更多文件操作
- 18:命名, 變量, 代碼, 函數
- 19:函數和變量
- 20:函數和文件
- 21:函數的返回值
- 22:到目前為止你學到了什么?
- 23:閱讀代碼
- 24:更多的練習
- 25:更多更多的練習
- 26:恭喜你,可以進行一次考試了
- 27:記住邏輯
- 28:布爾表達式
- 29:IF 語句
- 30:Else 和 If
- 31:做出決定
- 32:循環和列表
- 33:while循環
- 34:訪問列表元素
- 35:分支和函數
- 36:設計和調試
- 37:復習符號
- 38:列表操作
- 39:字典,可愛的字典
- 40:模塊, 類和對象
- 41:學會說面向對象
- 42:對象、類、以及從屬關系
- 43:基本的面向對象的分析和設計
- 44:繼承Vs.包含
- 45:你來制作一個游戲
- 46:項目骨架
- 47:自動化測試
- 48:更復雜的用戶輸入
- 49:寫代碼語句
- 50:你的第一個網站
- 51:從瀏覽器獲取輸入
- 52:開始你的web游戲
- 來自老程序員的建議
- 下一步
- 附錄A:命令行教程
- 簡介
- 安裝和準備
- 路徑, 文件夾, 名錄 (pwd)
- 如果你迷路了
- 創建一個路徑 (mkdir)
- 改變當前路徑 (cd)
- 列出當前路徑 (ls)
- 刪除路徑 (rmdir)
- 目錄切換(pushd, popd)
- 生成一個空文件(Touch, New-Item)
- 復制文件 (cp)
- 移動文件 (mv)
- 查看文件 (less, MORE)
- 輸出文件 (cat)
- 刪除文件 (rm)
- 退出命令行 (exit)
- 下一步