Python Web编程
摘要
本文提供了一些集成Python和Web服务器的方法,以及一些开发网站有用的实践,来展示如何使用Python进行web开发。
Web 2.0专注于让网站上的用户生成内容,自从它开始,网络编程就成为了热门话题。一直以来都可以使用Python创建网站,但这是一个相当无聊的任务。因此,有很多框架和工具被创造出来,帮助开发人员创建更快,更强大的网站。本文描述了一些将Python和Web服务器组合以创建动态内容的方法。这不是一个完整的介绍,因为这个话题过于广泛,不可能在一篇文章中讲清楚。本文会介绍目前最流行的库。
底层一窥
当用户打开网站时,他们使用的浏览器会连接到该网站的Web服务器(这被称之为请求)。服务器在文件系统中查找文件,并将其发回用户的浏览器(这被称之为响应)。这是底层HTTP协议的大概工作过程。
动态网站不是基于文件系统中的文件,而是基于在请求进入时Web服务器运行的程序,由程序生成内容返回给用户。这些程序可以做各种有用的事情,比如显示公告板的发布,显示电子邮件,配置软件,或者只显示当前时间。这些程序可以用服务器支持的任何编程语言编写。因为大多数的服务器都支持Python,因此可以使用Python轻松地创建动态网站。
大多数的HTTP服务器软件是用C或者C++编写的,所以它们不能直接执行Python代码。服务器程序和应用程序之间需要一个桥梁。这些桥梁,或者叫做接口,定义了程序如何与服务器进行交互。对于这样的接口,已经有了很多很好的尝试,但是只有一些值得一提。
不是所有的Web服务器都支持所有的接口。很多Web服务器只支持旧的,已过时的接口;不过,它们通常可以使用第三方模块进行扩展,以支持较新的模块。
通用网关接口
通用网关接口,通常叫做CGI,是最古老的接口,几乎所有的Web服务器都支持。Web服务器通过启动CGI程序来处理请求。对于Python程序来说,每个请求都会启动一个新的Python解释器,这需要一些时间,因而整个服务只能用于低负载的情况。
CGI的优势在于它很简单,编写一个使用CGI的Python程序只需要大概3行代码。这种简单是有代价的,就是它对于开发者的支持不够。
现在已经不再推荐编写CGI程序了。使用WSGI可以编写兼容CGI的程序,并作为CGI程序运行。
简单的CGI脚本
可以用下面的小程序测试Web服务器是否支持CGI
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import cgitb
cgitb.enable()
print "Content-Type: text/plain;charset=utf-8"
print
print "Hello World!"
根据Web服务器的配置,可能需要使用.py或.cgi扩展名来保存上面的代码。另外,处于安全考虑,代码文件可能需要保存到cgi-bin
文件夹。
你可能会疑惑cgitb
是用来干嘛的。在发生异常时,该库会返回一些栈回溯信息,而不是在浏览器上简单返回"Internal Server Error"。这对调试非常有用,但可能会将一些机密数据暴露给用户。所以不能在生产环境中使用cgitb
。开发者应该总是捕获异常,并显示正确的错误页面,终端用户不喜欢在浏览器中看到"Internal Server Errors"。
CGI脚本的常见问题
使用CGI的过程中可能会有一些坑导致脚本不能运行。有时,一个看似正确的脚本可能因为一些很难发现的问题导致不能正常工作。
下面是其中的一些问题:
- Python脚本没有可执行权限。当CGI脚本不可执行时,大多数Web服务器会让用户下载这个脚本,而不是运行脚本将输出返回给用户。要使CGI脚本在类Unix操作系统上正常运行,可以使用
chmod a+x your_script.py
来解决这个问题。 - 在类Unix系统上,程序文件中的行结尾必须是Unix样式行尾。这很重要,因为Web服务器会检查脚本的第一行(称为shebang),并尝试运行在那里指定的程序。它很容易被Windows的换行符(回车和换行,也称为CRLF)困惑,所以必须将文件转换为Unix行结尾(LF)。在通过FTP上传文件时,可以通过文本模式而不是二进制模式来自动完成这一转换,但是首选的方法是告诉编辑器使用Unix行结尾来保存文件。大多数编辑器都支持这一功能。
- Web服务器必须能够读取该脚本,所以需要确保权限正确。在类Unix系统上,服务器程序的用户和组通常是www-data,所以可能得更改文件的所有权,或者使用
chmod a+r your_script.py
。 - Web服务器器必须要知道访问的是CGI脚本。检查服务器配置,因为服务器可能只期望具有特定扩展名的文件为CGI脚本。
- 在类Unix系统中,shebang(
#!/usr/bin/env python
)中的路径必须是正确的。该行调用/usr/bin/env
来查找Python的路径,但是如果Python在服务器的路径不存在,或者/usr/bin/env
不存在的话,调用将会失败。
mod_python
PHP的使用者通常会发现,很难使用Python进行Web开发。他们首先想到的是使用mod_python,因为他们认为这和mod_php
是一样的。事实上,这两个是有不同的。mod_python
将Python解释器嵌入到Apache进程中,这样不必为每个请求启动Python解释器,从而加快请求。另一方面,它不是Python与HTML的混合,PHP通常与HTML混合。Python中达到类似效果的是模版引擎。mod_python
本身更为强大,并提供对Apache内部组件的更多访问。它可以模仿CGI,工作在“Python-页面”模式(类似于JSP),这个模式让Python可以和HTML混合使用,它有一个“发布者”来指定一个文件来接受和处理所有请求。
不过mod_python
有一些问题。和PHP解释器不同,Python解释器在执行文件的时候会有缓存,因此对文件的更改需要重新启动Web服务器。另一个问题是,Apache通过启动子进程来处理请求,不幸的是,所有的子进程都要加载整个Python解释器。这会让服务器更慢。还有一个问题,mod_python
与特定版本的libpython
链接,所以不可能在不重新编译的情况下从旧版本切换到新版本。mod_python
和apache是绑定的,所以为mod_python
编写的程序在其他服务器上不能轻易运行。
这些就是避免使用mod_python
编写程序的原因。在某些情况下,可以考虑使用mod_python
进行部署,但是WSGI程序也可以在mod_python
下运行。
FastCGi和SCGI
FastCGI和SCGI尝试通过另外一种方式解决CGI的性能问题。它不再将解释器嵌入到Web服务器中,而是创建长时间运行的后台程序。Web服务器中的一个模块使得其可以与后台进程通信。由于后台进程独立于服务器,因此它可以用任意语言进行编写,包括Python。该语言只需要一个处理与Web服务器通信的库。
FastCGi和SCGI的区别很小,因为SCGI本质上只是一个更简单的FastCGI(simpler FastCGI)。支持SCGI的Web服务器不多,因此大多数人都使用FastCGI。几乎所有适用于SCGI的内容也适用于FastCGI,所以这里只涉及后者。
现在FastCGI不再被直接使用了。就像mod_python
一样,它只用于部署WSGI应用程序。
设置FastCGI
每种Web服务器都有自己的特定模块。
- Apache有mod_fastcgi和mod_fcgid。最开始是
mod_fastcgi
,但是它有一些许可问题,有时被认为是非免费的。mod_fcgid
是一个较小的,兼容的替代方案。Apache需要加载其中一个模块。 - lighttpd同时有FastCGI module和SCGI module
- nginx也支持FastCGI。
安装和配置好上面模块后,可以用下面的程序测试一下。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from cgi import escape
import sys, os
from flup.server.fcgi import WSGIServer
def app(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
yield '<h1>FastCGI Environment</h1>'
yield '<table>'
for k, v in sorted(environ.items()):
yield '<tr><th>%s</th><td>%s</td><tr>' % (escape(k), escape(v))
yield '</table>'
WSGIServer(app).run()
这就是一个简单的WSGi应用程序,但是首先需要安装flup来处理底层的FastCGI访问。
mod_wsgi
mod_wsgi试图摆脱网关。FastCGi,SCGI和mod_python主要用于部署WSGI应用程序,mod_wsgi直接将WSGI应用程序嵌入Apache Web服务器。mod_wsgi专门用于承载WSGI应用程序。和其他需要使用胶水代码来部署的方法比较起来,使用mod_wsgi部署起来更加容易。缺点是mod_wsgi仅限于Apache Web服务器;其他服务器需要自己的mod_wsgi实现。
mod_wsgi支持两种模式:集成Apache进程的嵌入模式和FastCGi类似的守护模式。和FastCGi不同,mod_wsgi可以自己处理工作进程,这使得管理起来更加容易。
WSGI
在前面已经多次提到了WSGI,现在来解释它的重要性。
WSGI全称是Web服务器网关接口,PEP 333中定义了它,目前是Python Web编程的最佳方式。它一般会用来编写框架,一般的Web开发人员并不需要直接接触它。在选择Web开发框架时,最好选择支持WSGI的框架。
WSGI的最大优点就是统一了应用程序编程接口。如果使用的框架支持WSGI,那么应用程序就能在所有支持WSGI的Web服务器上进行部署。应用程序并不需要关心部署使用的是mod_python或FastCGi或者mod_wsgi--使用WSGi,应用程序将能在在任何网关接口上运行。Python标准库包含自己的WSGi服务器wsgiref,它是一个可用于测试的小型Web服务器。
WSGI一个非常好的特性是中间件。中间件是应用程序的一层,可以在其中添加各种功能。已经有很多的中间件存在了。比如,不用再编写自己的会话管理(HTTP是无状态协议,因此要将多个HTTP请求与单个用户关联,应用程序必须通过会话创建和管理此类状态),只需要下载会话管理中间件,将其插入应用,然后就能进行应用相关的特定逻辑的编码了。同样的,现在也有中间件可以使用gzip处理压缩HTML,来节省服务器的宽带。也可以使用现有中间件轻松解决认证问题。
虽然WSGI可能看起来很复杂,但初始阶段的学习是非常有益的,因为对开发网中中出现的问题,WSGI和相关的中间件很有可能已经有了解决方案。
WSGI服务器
用于连接到各种底层网关(如CGI和mod_python)的代码被称为WSGI服务器。flup
就是一个WSGI服务器,它支持FastCGi,SCGI和AJP。部分服务器使用Python编写,比如flup
,还存在其他以C编写的服务器,可以用作插入式替换。
已经有很多WSGI服务器存在了,所以Python Web应用几乎可以在任何地方部署。这是Python与其他网络技术相比的一大优势。
模型-视图-控制器
通常在“框架**支持MVC”这样的语句中听到MVC这个术语。MVC更多地关心代码的整体组织,而不是任何特定的API。很多Web框架使用这个模型来帮助开发人员管理程序的结构。大型的Web应用程序可以有很多代码,因此最好在一开始就拥有一个有效的结构。这样,即使是其他框架的用户(甚至其他语言)也可以很容易地理解代码,因为他们对MVC已经很熟悉了。
MVC把软件系统分为三个基本部分:
- 模型。用于呈现和修改的数据。在Python框架中,这个组件通常类来表示对象关系映射器。
- 视图。该组件的任务是向用户展示模型的数据。通常,该组件通过模版实现。
- 控制器。负责转发请求,对请求进行处理。
可能有人会认为MVC是一个复杂的设计模式,其实并不是。它被用于Python,因为它已被证明可以用于创建干净,可维护的网站。
构建网站的工具
网站是很复杂的,有一些工具可以帮助Web开发人员使代码更易于编写和维护。所有编程语言,所有框架中都有这样的工具。开发者并不会被强制要求使用这些工具,通常也不存在最好的工具。不过开发者有必要学一些这样的工具,来简化网站的开发过程。
模版
可以通过一个库来使HTML和Python代码混合。这样做刚开始的时候很方便,但是会导致代码变得不可维护。这就是模版诞生的背景。在最简单的情况下,模版只是带有占位符的HTML文件。在填写占位符后,HTML文件将会返回给用户的浏览器。
Python已经有两种方式构建简单的模版了:
>>> template = "<html><body><h1>Hello %s!</h1></body></html>"
>>> from string import Template
>>> template = Template("<html><body><h1>Hello ${name}</h1></body></html>")
>>> print template.substitute(dict(name='Dinsdale'))
<html><body><h1>Hello Dinsdale</h1></body></html>
为了基于复杂的模型数据生成复杂的HTML,通常需要像Python的for
和if
这样的控制语句。模版引擎支持这样复杂的模版。
Python有很多可用的模版引擎。其中的一些定义了一种易于学习的纯文本编程语言,易学习的部分原因在于其范围有限。其他的引擎使用XML,模版输出保证始终是有效的XML。还有很多其他的变种。
一些框架自带模版引擎或者推荐像用户推荐一个。在没有理由使用其他引擎的情况下,最好使用由框架提供或者推荐的模版引擎。
流行的模版引擎包括
数据持久化
听起来非常复杂,但是数据持久化只是数据存储。该数据可能是博客条目的内容,公告板上的帖子,或者Wiki页面的文本。当然,在Web服务器上存储信息的方法有很多种。
通常会使用像MySQL和PostgreSQL这样的关系数据库引擎,因为它们在处理由数百万个条目组成的大型数据时具有良好的性能。也有像SQLite这样的小型数据库引擎,它通过sqlite3模块和Python捆绑,仅使用一个文件。它没有其他的依赖,对于小网站而言,使用SQLite就足够了。
关系型数据库通过SQL查询数据。Python程序员通常不太喜欢使用SQL,因为他们喜欢使用对象。可以使用ORM(对象关系映射)技术将Python对象保存在数据库中。ORM将所有面向对象的访问转换成SQL语句。大多数的框架都使用ORM。
第二种做法是将数据存储在普通的文本文件中(有时称为“平面文件”)。这对于简单站点来说非常好用,但是如果网站对存储的数据执行许多更新操作,则难以保持数据的正确。
第三种方案是面向对象的数据库(也称为“对象数据库”)。这些数据库将数据与程序运行时内存中的数据以相同的结构存储。(相比之下,ORM将对象数据作为表中的数据行和这些行之间的关系存储。)直接存储对象的优点在于几乎所有的对象都可以以直接的方式保存,而关系型数据库中,很难用对象表示数据。
框架通常会对使用的存储类型作出提示。最好使用框架推荐的类型,除非应用有特殊的有求。
框架
创建网站包括编写代码以提供各种服务。不论网站的复杂程度或者目的,提供特定服务的代码通常以相同的方式工作。将这些常见的解决方案提取到可重用的代码中,产生了用于Web开发的所谓的“框架”。也许最有名的Web开发框架是Ruby on Rails,但是Python有自己的框架。其中的一部分来自Rails的启发,或者借鉴了Rails的想法,但有很多框架在Rails之前就出现了。
最初,Python Web框架倾向于将开发网站所需的所有服务纳入到一个巨大的集成工具中。任何两个框架之间都是不能互相合作的:为一个框架开发的程序,不能不经巨大修改就部署在另外一个的框架上。这导致了“极简”Web框架的开发,这种框架提供工具来完成Python代码和http协议之间的通信,其他的服务单独将以组件的形式加入应用。一些特殊的标准被开发出来,来允许框架之间的有限互操作性,例如允许不同的模版引擎互换使用的标准。
自从WSGI出现以来,Python Web框架向基于WSGI标准的互操作性的方向发展。现在,许多Web框架,无论是“全栈”(提供部署最复杂的网站所需的所有工具)和最简单的,都是由可重复使用的组件的集合构建的,这些组件可以在多个框架中使用。
大多数用户想要选择的是一个具有活跃社区的“全栈”框架。这些框架往往由很好的文档,并提供了简单的方式在最短时间内生成功能完整的网站。
一些有名的框架
框架的数量多的令人难以置信,因此这里只介绍几个最流行的框架。
Django
Django是由几个耦合紧密的组件组成的框架。它包含了一个强大易用的ORM和数据在线管理平台。模版引擎是基于文本的,为不懂Python的设计人员设计。它支持模版继承和过滤器(类似于Unix管道)。Django捆绑了很多方便的功能,如创建RSS源或通用视图,这使得不写一行Python代码来创建网站变的可能。
它有一个人数众多的国际化社区,其中的成员已经创建了很多网站。还有很多附加项目可以扩展Django的正常功能。这部分归功于Django维护良好的在线文档和Django book。
TurboGears
另外一个流行的库是TurboGears。TurboGears采用已经存在的组件,并通过胶水代码来创建无缝体验的方法。TurboGears为用户提供了选择组件的灵活性。例如,ORM和模版引擎可以更改为使用与默认库不同的库。
文档见这里,其中可以找到Screencast的链接。
“本译文仅供个人研习、欣赏语言之用,谢绝任何转载及用于任何商业用途。本译文所涉法律后果均由本人承担。本人同意简书平台在接获有关著作权人的通知后,删除文章。”