习题 50: 你的第一个网站

这节以及后面的习题中,你的任务是把前面创建的游戏做成网页版。这是本书的最后三个章节,这些内容对你来说难度会相当大,你要在上面花些时间才能做出来。在你开始这节练习以前,你必须已经成功地完成过了《习题 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”项目

现在你将做一个非常简单的“Hello World”项目出来,首先你要创建一个项目目录:

$ cd projects
$ mkdir gothonweb
$ cd gothonweb
$ mkdir bin gothonweb tests docs templates
$ touch gothonweb/__init__.py
$ touch tests/__init__.py

你最终的目的是把《习题 42》中的游戏做成一个 web 应用,所以你的项目名称叫做 gothonweb,不过在此之前,你需要创建一个最基本的 lpthw.web 应用,将下面的代码放到 bin/app.py 中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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 项目中,你都不需要进到底层目录去运行东西。你应该停留在最上层目录运行,这样才能保证所有的模组和文件能被正常访问到。如果你犯了这个错误,就请回到《习题 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 来访问。它使用到的网络端口是 5000
  2. 连接成功以后,浏览器对 bin/app.py 这个应用程序发出了 HTTP 请求(request),要求访问 URL /,这通常是一个网站的第一个 URL。
  3. bin/app.py 里,我们有一个列表,里边包含了 URL 和类的匹配关系。我们这里只定义了一组匹配,那就是 '/', 'index' 的匹配。它的含义是:如果有人使用浏览器访问 / 这一级目录,lpthw.web 将找到并加载 class index,从而用它处理这个浏览器请求。
  4. 现在 lpthw.web 找到了 class index,然后针对这个类的一个实例调用了 index.GET 这个方法函数。该函数运行后返回了一个字符串,以供 lpthw.web 将其传递给浏览器。
  5. 最后 lpthw.web 完成了对于浏览器请求的处理,将响应(response)回传给浏览器,于是你就看到了现在的页面。

确定你真的弄懂了这些,你需要画一个示意图,来理清信息是如何从浏览器传递到 lpthw.web,再到 index.GET,再回到你的浏览器的。

修正错误

第一步,把第 11 行的 greeting 变量赋值删掉,然后刷新浏览器。你应该会看到一个错误页面,你可以通过这一页丰富的错误信息看出你的程序崩溃的原因是什么。当然你已经知道出错的原因是 greeting 的赋值丢失了,不过 lpthw.web 还是会给你一个挺好的错误页面,让你能找到出错的具体位置。试试在这个错误页面上做以下操作:

  1. 检查每一段 Local vars 输出(用鼠标点击它们),追踪里边提到的变量名称,以及它们是在哪些代码文件中用到的。
  2. 阅读 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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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 对象。
  2. 你将 templates/ 作为参数传递给了这个对象,这样就让 render 知道了从哪里去加载模板文件。
  3. 在你后面的代码中,当浏览器一如既往地触发了 index.GET 以后,它没有再返回简单的 greeting 字符串,取而代之的是你调用了 render.index,而且将问候语句作为一个变量传递给它。
  4. 这个 render_template 函数可以说是一个“魔法函数”,它看到了你需要的是 index.html,于是就跑到 templates/ 目录下,找到名字为 index.html 的文件,然后就把它渲染(render)一遍(叫“转换一遍”也可以)。
  5. templates/index.html 文件中,你可以看到初始定义一行中说这个模板需要使用一个叫 greeting 的参数,这和函数定义中的格式差不多。另外和 Python 语法一样,模板文件是缩进敏感的,所以要确认自己弄对了缩进。
  6. 最后,你让 templates/index.html 去检查 greeting 这个变量,如果这个变量存在的话,就打印出变量的内容,如果不存在的话,就会打印出一个默认的问候信息。

要深入理解这个过程,你可以修改 greeting 变量以及 HTML 模板的内容,看看会有什么效果。然后创建一个叫做 templates/foo.html 的模板,并且使用一个新的 render.foo 去渲染它。从这个过程你也可以看出,render 调用的函数名称只要跟 templates/ 下的 .html 文件名匹配到,这个 HTML 模板就可以被渲染到了。

加分习题

  1. http://webpy.org/ 阅读里边的文档,它其实和 lpthw.web 是同一个项目。
  2. 实验一下你在上述网站看到的所有的东西,包括里边的代码示例。
  3. 阅读以下 HTML5 和 CSS3 相关的东西,自己练习着写几个 .html 和 .css 文件。
  4. 如果你有一个懂 Django 朋友可以帮你的话,你可以试着使用 Django 完成一下习题 50、51、52,看看结果会是什么样子的。

常见问题回答

我没法连接 http://localhost:8080/.

那就试试 http://127.0.0.1:8080/

lpthw.webweb.py 有啥不同?

一样的。我只不过“锁定”了 web.py 的某个版本,把它命名为 lpthw.web ,这样同学们用的版本就都是一样的了。这样就算日后 web.py 升级升到面目全非,我也无需更新本书了。

我找不到 index.html (或者别的文件)。

很有可能是你先跑了 cd bin/ 然后才开始做项目的。不要这么做,所有的指令都应该在 bin/ 的上一层完成,所以如果你无法运行 python bin/app.py 那就说明你不在正确的目录下面。

为什么调用 template 时要写 greeting=greeting

这一句并不是赋值给 greeting ,而是将一个命名参数传到模板中。这也算是一种赋值,不过只会在模板函数的调用中生效。

端口 8080 无法使用。

也许是哪个杀毒软件占用了这个端口,那就换一个端口好了。

安装 lpthw.web 时出现 ImportError "No module named web"

很有可能是你在系统中安装了多个版本的 Python,而在这里你用了错误的一个,或者由于 pip 版本太旧导致安装没有正确完成。试着卸载并重装 lpthw.web 。如果还不行,那就再仔细检查确认自己用了正确版本的 Python。

Project Versions

Table Of Contents

Previous topic

习题 49: 创建句子

Next topic

习题 51: 从浏览器中获取输入

This Page