朱皮特的博客 自由的飞翔

Python Web开发

2018-07-02
朱皮特
阅读量:

web结构

  • 前端程序:HTML、CSS、JS
  • 后台程序:Python、PHP、JSP……
  • 数据库:MySQL、MongoDB

Web 应用:客户端/服务器计算

Web 应用遵循前面反复提到的客户端/服务器架构。这里说的 Web 客户端是浏览器,即允许用户在万维网上查询文档的应用程序。另一边是 Web 服务器端,指的是运行在信息提供商的主机上的进程。这些服务器等待客户端和及其文档请求,进行相应的处理,并返回相关的数据。正如大多数客户端/服务器系统中的服务器端一样, Web 服务器端“永远”运行。图9-1 展示了 Web 应用的惯用流程。这里,用户运行 Web 客户端程序(如浏览器),连接因特网上任意位置的 Web 服务器来获取数据。

客户端可以向 Web 服务器端发出各种不同的请求。这些请求可能包括获得一个用于查看的网页视图,或者提交一个包含待处理数据的表单。 Web 服务器端首先处理请求,然后会以特定的格式(HTML 等)返回给客户端浏览。

Web 客户端和服务器端交互需要用到特定的“语言”,即 Web 交互需要用到的标准协议,称为 HTTP(HyperText Transfer Protocol, 超文本传输)。 HTTP 是 TCP/IP 的上层协议,这意味着 HTTP 协议依靠 TCP/IP 来进行低层的交流工作。它的职责不是发送或者递消息(TCP/IP 协议处理这些),而是通过发送、接受 HTTP 消息来处理客户端的请求。

HTTP 属于无状态协议,因为其不跟踪从一个客户端到另一个客户端的请求信息,这点很像现在使用的客户端/服务器架构。服务器持续运行,但是客户端的活动以单个事件划分的,一旦完成一个客户请求,这个服务事件就停止了。客户端可以随时发送新的请求,但是新的请求会处理成独立的服务请求。由于每个请求缺乏上下文,因此你可能注意到有些 URL 中含有很长的变量和值,这些将作为请求的一部分,以提供一些状态信息。另一种方式是使用“cookie”,即保存在客户端的客户状态信息。在本章的后面部分将会看到如何使用 URL 和 cookie 来保存状态信息。

因特网

因特网上传输的数据当中,其中一部分会比较敏感。而在传输过程中,默认没有加密服务,所以标准协议直接将应用程序发送过来的数据传输出去。为了对传输数据进行加密,需要在普通的套接字上添加一个额外的安全层,称为安全套接字层(SecureSocketLayer, SSL),用来创建一个套接字,加密通过该套接字传输的数据。开发者可以决定是否使用这个额外的安全层。

URL 统一资源定位符

简单的网页浏览需要用到名为 URL(统一资源定位符)的 Web 地址。这个地址用来在 Web上定位一个文档,或者调用一个 CGI 程序来为客户端生成一个文档。 URL 是多种统一资源标识符(Uniform Resource Identifier, URI) 的一部分。这个超集也可以应对其他将来可能出现的标识符命名约定。一个 URL 是一个简单的 URI,它使用已有的协议或方案(即 http、 ftp 等)作为地址的一部分。为了更完整地描述,还要介绍非 URL 的 URI,有时它们称为统一资源名称(Uniform Resource Name, URN),但是现在唯一使用的 URI 只有 URL,而很少听到 URI和 URN,后者只作为可能会用到的 XML 标识符了。

如街道地址一样, Web 地址也有一些结构。美国的街道地址通常形如“号码 街道名称”,例如“123 某某大街”。其他国家的街道地址也有自己的规则。 URL 使用这种格式。

prot_sch://net_loc/path;params?query#fra

  • prot_sch 网络协议或下载方案
  • net_loc 服务器所在地(也许含有用户信息)
  • path 使用斜杠(/)分割的文件或 CGI 应用的路径
  • params 可选参数
  • query 连接符(&) 分割的一系列键值对
  • frag 指定文档内特定锚的部分

在 Python 3 中,所有这些相关模块都整合进了一个名为 urllib 的单一包中。 urlib 和 urlib2 中的内容整合进了 urlib.request模块中, urlparse 整合进了 urllib.parse 中。 Python 3 中的 urlib 包还包括 response、 error 和robotparse 这些子模块。

解析web页面

  • HTMLParser.HTMLParser
  • htmllib.HTMLParser

可编程的web浏览

  • BeautifulSoup
  • Mechanize

Web(HTTP)服务器

  • http.server
  • http客户端:http.client

CGI和WSGI

WSGI 主要有利于 Web 框架和 Web 服务器的作者,不是 Web 应用的作者。 WSGI 不是一个应用程序 API,而是框架与服务器之间的粘合 API。 —Phillip J. Eby, 2004 年 8 月

一般生产环境的 Web应用都不再使用 CGI 了

现在读者已经对 CGI 有深入的了解,并知道为什么需要 CGI。因为服务器无法创建动态内容,它们不知道用户特定的应用信息和数据,如验证信息、银行账户、在线支付等。 Web服务器必须与外部的进程通信才能处理这些自定义工作。

但是这种方式无法扩展, CGI 进程(类似 Python 解释器)针对每个请求进行创建,用完就抛弃。如果应用程序接收数千个请求,创建大量的语言解释器进程很快就会导致服务器停机。有两种方法可以解决这个问题,一是服务器集成, 二是外部进程。下面分别介绍这两种方法。

另一个解决方案是外部进程。这是让 CGI 应用在服务器外部运行。当有请求进入时,服务器将这个请求传递到外部进程中。这种方式的可扩展性比纯 CGI 要好,因为外部进程存在的时间很长,而不是处理完单个请求后就终止。使用外部进程最广为人知的解决方案是FastCGI。有了外部进程,就可以利用服务器 API 的好处,同时避免了其缺点。比如,在服务 器外部运行就可以自由选择实现语言,应用程序的缺陷不会影响到 Web 服务器,不需要必须与闭源的商业软件结合起来。

很自然,FastCGI 有 Python 实现,除此之外还有 Apache 的其他 Python 模块(如 PyApache、mod_snkae、 mod_python 等),其中有些已经不再维护了。所有这些模块加上纯 CGI 解决方案,组成了各种 Web 服务器 API 网关解决方案,以调用 Python Web 应用程序。

由于使用了不同的调用机制,所以开发者有了新的负担。不仅要开发应用本身,还要决定与 Web 服务器的集成。实际上,在编写应用时,就需要完全知道最后会使用哪个机制,并以相应的方式执行。

对于 Web 框架开发者,问题就更加突出了,由于需要给予用户最大的灵活性。如果不想强迫他们开发多版本的应用,就必须为所有服务器解决方案提供接口,以此来让更多的用户采用你的框架。这个困境看起来绝不是 Python 的风格,就导致了 Web 服务器网类接口(WebServer Gateway Interface, WSGI)标准的建立。

搭建一个简单的web服务

cd F:\PycharmProjects\test

例如在F:\PycharmProjects\test目录下创建cgi-bin目录,在cgi-bin创建一个webs.py文件:

import cgi,cgitb

form1 = cgi.FieldStorage()

name = form1.getvalue('name')

print 'Content-type:text/html\n\n'
print 'hello ' + name

打开命令行: cd F:\PycharmProjects\test 执行python -m CGIHTTPServer 8081

服务器开启后,在浏览器中输入: http://localhost:8081/cgi-bin/webs.py?name=1

WSGI

WSGI 不是服务器,也不是用于与程序交互的 API,更不是真实的代码,而只是定义的一个接口。 WSGI 规范作为 PEP 333 于 2003 年创建,用于处理日益增多的不同 Web 框架、Web 服务器,及其他调用方式(如纯 CGI、服务器 API、外部进程)。

其目标是在 Web 服务器和 Web 框架层之间提供一个通用的 API 标准,减少之间的互操作性并形成统一的调用方式。 WSGI 刚出现就得到了广泛应用。基本上所有基于 Python 的Web 服务器都兼容 WSGI。将 WSGI 作为标准对应用开发者、框架作者和社区都有帮助。

根据 WSGI 定义,其应用是可调用的对象,其参数固定为以下两个:一个是含有服务器环境变量的字典,另一个是可调用对象,该对象使用 HTTP 状态码和会返回给客户端的 HTTP头来初始化响应。这个可调用对象必须返回一个可迭代对象用于组成响应负载。

中间件

在某些情况下,除了运行应用本身之外,还想在应用执行之前(处理请求)或之后(发送响应)添加一些处理程序。这就是熟知的中间件,它用于在 Web 服务器和 Web 应用之间添加额外的功能。中间件要么对来自用户的数据进行预处理,然后发送给应用;要么在应用将响应负载返回给用户之前,对结果数据进行一些最终的调整。这种方式类似洋葱结构,应 用程序在内部,而额外的处理层在周围。

预处理可以包括动作,如拦截、修改、添加、移除请求参数,修改环境变量(包括用户提交的表单(CGI)变量),使用 URL 路径分派应用的功能,转发或重定向请求,通过入站客户端 IP 地址对网络流量进行负载平衡,委托其功能(如使用 User-Agent 头向移动用户发送简化过的 UI/应用),以及其他功能。

而后期处理主要包括调整应用程序的输出。下面这个示例类似第 2 章创建的时间戳服务器,其中对于应用返回的每一行结果,都会添加一个时间戳。在实际中,这会更加复杂,但大致方式都相同,如添加将应用的输出转为大写或小写的功能。这里使用 ts_simple_wsgi_app()封装了 simple_wsgi_app(),将前者作为应用注册到服务器中。

现实世界中的 Web 开发

CGI 是过去用来开发 Web 的方式,其引入的概念至今仍应用于 Web 开发当中。因此,这就是为什么在这里花时间学习 CGI。而对 WSGI 的介绍让读者更接近真实的开发流程。

今天, Python Web 开发新手的选择余地很多。除了著名的 Django、 Pyramid 和 Google AppEngine 这些 Web 框架之外,用户还可以在其他众多的框架中选择。实际上,框架甚至都不是必需的,直接使用 Python,不用任何其他额外的工具或框架特性,就能开发出兼容 WSGI 的Web 服务器。但最好继续使用框架,这样可以方便地用到框架提供的其他 Web 功能。

现代的 Web 执行环境一般由多线程或多进程模型、认证/安全 cookie、基本的用户验证、会话管理组成。普通应用程序的开发者都会了解这其中大部分内容。验证表示的是用户通过用户名和密码进行登录, cookie 用来维护用户信息,会话管理有时候也是如此。为了使应用具有可扩展性, Web 服务器应当能够处理多个用户的请求。因此,需要用到多线程或多进程。但会话在这里还没有完全涉及。

如果读者阅读本章中运行在服务器上的所有应用程序代码,那么就需要说明一下,脚本从头到尾执行一次,服务器循环永远执行, Web 应用(Java 中称为 servlet)针对每个请求执行。代码中不会保存状态信息,前面已经提到过, HTTP 是无状态的。换句话说,数据是不会保存到局部或全局变量中,或以其他方式保存起来。这就相当于把一个请求当成一个事务。每次来了一个事务,就进行处理,处理完就结束,在代码库中不保存任何信息。

此时就需要用到会话管理,会话管理一段时间内在一个或多个请求之间保存用户的状态。一般来说,这是通过某种形式的持久存储完成的,如缓存、平面文件甚至是数据库。开发者需要自己处理这些内容,特别是编写底层代码时,本章中已经见到过这些代码。毫无疑问,已经做了很多无用功,因此许多著名的大型Web框架,包括Django,都有自己的会话管理软件(下一章将介绍Django的相关内容。)

web框架

Web 开发除了像上一章那样全部从头写起,还可以在其他人已有的基础上进行开发,简化开发流程。这些 Web 开发环境统称为 Web 框架,其目标是帮助开发者简化工作,如提供一些功能来完成一些通用任务,或提供一些资源来用于降低创建、更新、执行或扩展应用的工作量。

由于 CGI 在可扩展性方面有缺陷,因此不建议使用它。所以 Python 社区的人们寻求一种更强大的 Web 服务器解决方案,如 Apache、 ligHTTPD(发音为“lighty”),或 nginx。有些服务器,如 Pylons 和 CherryPy,拥有自己的框架生态系统。但服务方面的内容只是创建 Web 应用的一个方面。还需要关注一些辅助工具,如 JavaScript 框架、对象关系映射器(ORM)或底层数据库适配器。还有与 Web 不相关但其他类型的开发需要的工具,如单元测试和持续集成框架。 Python Web 框架既可以是单个或多个子组件,也可以是一个完整的全栈系统

术语“全栈”表示可以开发 Web 应用所有阶段和层次的代码。框架可以提供所有相关的服务,如 Web 服务器、数据库 ORM、模板和所有需要的中间件 hook。有些还提供了 JavaScript库。 Django 就是这当中一个广为人知的 Web 框架。许多人认为 Django 对于 Python,就相当于 Ruby on Rails 对 Ruby 一样。 Django 包含了前面提到的所有服务,可作为全能解决方案(除了没有内置的 JavaScript 库,这样可以自由选择相应的库)。在第 12 章将看到 Google AppEngine 也提供了这些组件,但更适合于由 Google 托管并且侧重于可扩展性和快速请求/响应的 Web 和非 Web 应用。

Django

Django 自称是“能够很好地应对应用上线期限的 Web 框架”。


上一篇 Python线程

下一篇 Python日期时间

Comments

Content