如何用python和web.py搭建一个网站

如何用python和web.py搭建一个网站,第1张

1 环境搭建。

环境搭建比较繁琐,记得当时也是满世界找资料,所以我直接打包好了所有的文件(apache(已经放进去python-wscgi) + webpy+apache所需要的vs2010运行库+python+网站文件),直接解压放上去就能用。有需要同学让我传一个或者网盘发一把就行。

如果在windows,最好在win7以上版本,我用win8。linux则ubuntu较好,建议1204以上版本,不过linux我仅仅是开发用过,最终运行的服务器是采用windows+apache:

安装python2710,可以去官方网站下载,注意要把python路径加入系统环境变量。版本不能低于279,但不能用30以上,webpy支持不好。

安装webpy, 官方网下载来装就行,记得是解压后进去:python setuppy install

安装wingIDE,这个是最好用的python编辑器,可惜只能有几天试用,我就是不停的试用进行开发,用它的好处就是代码可以跳转并且支持调试,需要调试直接将程序文件run起来,单步下段点都可以。

装apache并配置python-wscgi,这个比较惨,花了好多时间去搜索才搞定,主要是windows上的python-wscgi不好难找。

为了节省以后的开发时间,我把配置好的apache给打包了,反正也是绿色的,新建网站只需要在服务器上配置开机启动apache,并在apache的config里面修改下自己网站路径就行。

2 开发。

开发阶段倒是没什么好说的,webpy官方有教程,不过这里我需要提几点建议:

如果可以务必全站用utf-8编码。

建立数据库建议写个生成脚本,比如createDataBasepy,有改动重新运行一遍,不要试用ide去建。

有人说表示只学Python没有用,必须学会一个框架(比如Django和webpy)才能找到工作。

其实掌握一个类似于框架的高级工具是有用的,但是基础的东西可以让你永远不被淘汰,不要被工具限制了自己的发展。

今天不使用框架,也不使用Python标准库中的高级包,只使用标准库中的socket接口写一个Python服务器。

框架与底层

在当今Python服务器框架 (framework, 比如Django, Twisted, webpy等等) 横行的时代,从底层的socket开始写服务器似乎是一个出力不讨好的笨方法。

框架的意义在于掩盖底层的细节,提供一套对于开发人员更加友好的API,并处理诸如MVC的布局问题。

框架允许我们快速的构建一个成型而且成熟的Python服务器。然而,框架本身也是依赖于底层(比如socket)。对于底层socket的了解,不仅可以帮助我们更好的使用框架,更可以让我们明白框架是如何设计的。

更进一步,如果拥有良好的底层socket编程知识和其他系统编程知识,你完全可以设计并开发一款自己的框架。

如果你可以从底层socket开始,实现一个完整的Python服务器,支持用户层的协议,并处理好诸如MVC(Model-View-Control)、多线程(threading)等问题,并整理出一套清晰的函数或者类,作为接口(API)呈现给用户,你就相当于设计了一个框架。

socket接口是实际上是操作系统提供的系统调用。

socket的使用并不局限于Python语言,你可以用C或者Java来写出同样的socket服务器,而所有语言使用socket的方式都类似(Apache就是使用C实现的服务器)。

但是你不能跨语言的使用框架。

框架的好处在于帮你处理了一些细节,从而实现快速开发,但同时受到Python本身性能的限制。

我们已经看到,许多成功的网站都是利用动态语言(比如Python, Ruby或者PHP,比如twitter和facebook)快速开发,在网站成功之后,将代码转换成诸如C和JAVA这样一些效率比较高的语言,从而让服务器能更有效率的面对每天亿万次的请求。

在这种情况下,底层的重要性,就远远超过了框架。

TCP/IP和socket简介

回到我们的任务。

我们需要对网络传输,特别是TCP/IP协议和socket有一定的了解。

socket是进程间通信的一种方法,它是基于网络传输协议的上层接口。

socket有许多种类型,比如基于TCP协议或者UDP协议(两种网络传输协议),其中又以TCP socket最为常用。

TCP socket与双向管道(duplex PIPE)有些类似,一个进程向socket的一端写入或读取文本流,而另一个进程可以从socket的另一端读取或写入,比较特别是,这两个建立socket通信的进程可以分别属于两台不同的计算机。

TCP协议,就是规定了一些通信的守则,以便在网络环境下能够有效实现上述进程间通信过程。

双向管道(duplex PIPE)存活于同一台电脑中,所以不必区分两个进程的所在计算机的地址,而socket必须包含有地址信息,以便实现网络通信。

一个socket包含四个地址信息: 两台计算机的IP地址和两个进程所使用的端口(port)。IP地址用于定位计算机,而port用于定位进程 (一台计算机上可以有多个进程分别使用不同的端口)。

TCP socket

在互联网上,让某台计算机作为服务器。

服务器开放自己的端口,被动等待其他计算机连接。

当其他计算机作为客户,主动使用socket连接到服务器的时候,服务器就开始为客户提供服务。

在Python中,我们使用标准库中的socket包来进行底层的socket编程。

首先是服务器端,我们使用bind()方法来赋予socket以固定的地址和端口,并使用listen()方法来被动的监听该端口。

当有客户尝试用connect()方法连接的时候,服务器使用accept()接受连接,从而建立一个连接的socket:

socketsocket()创建一个socket对象,并说明socket使用的是IPv4(AF_INET,IP version 4)和TCP协议(SOCK_STREAM)。

然后用另一台电脑作为客户,我们主动使用connect()方法来搜索服务器端的IP地址(在Linux中,你可以用$ifconfig来查询自己的IP地址)和端口,以便客户可以找到服务器,并建立连接:

在上面的例子中,我们对socket的两端都可以调用recv()方法来接收信息,调用sendall()方法来发送信息。

这样,我们就可以在分处于两台计算机的两个进程间进行通信了。

当通信结束的时候,我们使用close()方法来关闭socket连接。

(如果没有两台计算机做实验,也可以将客户端IP想要connect的IP改为"127001",这是个特殊的IP地址,用来连接当地主机。)

基于TCP socket的HTTP服务器

上面的例子中,我们已经可以使用TCP socket来为两台远程计算机建立连接。

然而,socket传输自由度太高,从而带来很多安全和兼容的问题。

我们往往利用一些应用层的协议(比如HTTP协议)来规定socket使用规则,以及所传输信息的格式。

HTTP协议利用请求-回应(request-response)的方式来使用TCP socket。

客户端向服务器发一段文本作为request,服务器端在接收到request之后,向客户端发送一段文本作为response。

在完成了这样一次request-response交易之后,TCP socket被废弃。

下次的request将建立新的socket。

request和response本质上说是两个文本,只是HTTP协议对这两个文本都有一定的格式要求。

Request <——> Response

现在,我们写出一个HTTP服务器端:

HTTP服务器程序的解释

如我们上面所看到的,服务器会根据request向客户传输的两条信息text_content和pic_content中的一条,作为response文本。

整个response分为起始行(start line), 头信息(head)和主体(body)三部分。起始行就是第一行:

它实际上又由空格分为三个片段,HTTP/1x表示所使用的HTTP版本,200表示状态(status code),200是HTTP协议规定的,表示服务器正常接收并处理请求,OK是供人来阅读的status code。

头信息跟随起始行,它和主体之间有一个空行。

这里的text_content或者pic_content都只有一行的头信息,text_content用来表示主体信息的类型为html文本:

而pic_content的头信息(Content-Type: image/jpg)说明主体的类型为jpg(image/jpg)。

主体信息为html或者jpg文件的内容。

(注意,对于jpg文件,我们使用"rb"模式打开,是为了与windows兼容。因为在windows下,jpg被认为是二进制(binary)文件,在UNIX系统下,则不需要区分文本文件和二进制文件。)

我们并没有写客户端程序,后面我们会用浏览器作为客户端。

request由客户端程序发给服务器。

尽管request也可以像response那样分为三部分,request的格式与response的格式并不相同。

request由客户发送给服务器,比如下面是一个request:

起始行可以分为三部分,第一部分为请求方法(request method),第二部分是URL,第三部分为HTTP版本。

request method可以有GET, PUT, POST, DELETE, HEAD。最常用的为GET和POST。

GET是请求服务器发送资源给客户,POST是请求服务器接收客户送来的数据。

当我们打开一个网页时,我们通常是使用GET方法;当我们填写表格并提交时,我们通常使用POST方法。

第二部分为URL,它通常指向一个资源(服务器上的资源或者其它地方的资源)。像现在这样,就是指向当前服务器的当前目录的testjpg。

按照HTTP协议的规定,服务器需要根据请求执行一定的操作。

正如我们在服务器程序中看到的,我们的Python程序先检查了request的方法,随后根据URL的不同,来生成不同的response(text_content或者pic_content)。

随后,这个response被发送回给客户端。

使用浏览器实验

为了配合上面的服务器程序,我已经在放置Python程序的文件夹里,保存了一个testjpg文件。

我们在终端运行上面的Python程序,作为服务器端,再打开一个浏览器作为客户端。

(如果有时间,你也完全可以用Python写一个客户端。原理与上面的TCP socket的客户端程序相类似。)

在浏览器的地址栏输入:

(当然,你也可以用令一台电脑,并输入服务器的IP地址)

OK,我已经有了一个用Python实现的,并从socket写起的服务器了。

从终端,我们可以看到,浏览器实际上发出了两个请求。

第一个请求为 (关键信息在起始行,这一个请求的主体为空):

我们的Python程序根据这个请求,发送给服务器text_content的内容。

浏览器接收到text_content之后,发现正文的html文本中有<IMG src="textjpg" />,知道需要获得textjpg文件来补充为,立即发出了第二个请求:

我们的Python程序分析过起始行之后,发现/testjpg符合if条件,所以将pic_content发送给客户。

最后,浏览器根据html语言的语法,将html文本和图画以适当的方式显示出来。

探索的方向

1) 在我们上面的服务器程序中,我们用while循环来让服务器一直工作下去。

实际上,我们还可以根据多线程的知识,将while循环中的内容改为多进程或者多线程工作。

2) 我们的服务器程序还不完善,我们还可以让我们的Python程序调用Python的其他功能,以实现更复杂的功能。比如说制作一个时间服务器,让服务器向客户返回日期和时间。你还可以使用Python自带的数据库,来实现一个完整的LAMP服务器。

3) socket包是比较底层的包。Python标准库中还有高层的包,比如SocketServer,SimpleHTTPServer,CGIHTTPServer,cgi。这些都包都是在帮助我们更容易的使用socket。如果你已经了解了socket,那么这些包就很容易明白了。利用这些高层的包,你可以写一个相当成熟的服务器。

4) 在经历了所有的辛苦和麻烦之后,你可能发现,框架是那么的方便,所以决定去使用框架。或者,你已经有了参与到框架开发的热情。

1、说明:windows下设置python环境变量,就是把python的安装目录添加到系统path中。2、步骤:1)确定python安装目录,根据版本不同安装目录也不同,可以在开始菜单中的快捷方式中查看。在python快捷方式上点右键,属性菜单

2)在目录中可以看到安装位置,C:\Program Files\Python35\,如下图:

3)在桌面计算机点右键属性,也可以在控制面板中选系统

4)点高级系统设置:

5)高级标签,点环境变量按钮:

6)在系统变量中找到Path然后点编辑:

7)在变量值末尾添加;C:\Program Files\Python35\,就是你python安装的目录,注意如果原来末尾没有分号要添加一个分号。然后点确定,再把之前的对话框也确定。

8)这样环境变量就设置完成了,win+r打开运行对话框输入cmd打开命令行,在命令行中输入python,出现如下就说明设置成功了。

3、注意事项:如果未出现python结果,则需要检查路径是否设置正确,并重新启动一下计算机即可。

使用pip或easy_install可以管理和安装python的package包,实际上它们都是从pypi服务器中搜索和下载package的。目前在pypi服务器上,有超过三万多个package,同时还允许我们将自己的代码也上传发布到服务器上。这样,世界上的所有人都能使用pip或easy_install来下载使用我们的代码了。

具体步骤如下:

首先创建项目文件和setup文件。

目录文件结构如下:

project/

simpletest/

__init__py

testpy

setuppy

假设项目文件只有一个simpletest包,里面有一个testpy文件。

创建的setuppy文件格式大致如下,其中,install_requires字段可以列出依赖的包信息,用户使用pip或easy_install安装时会自动下载依赖的包。详细的格式参考文档。

from setuptools import setup, find_packages

setup(

name = 'simpletest',

version = '001',

keywords = ('simple', 'test'),

description = 'just a simple test',

license = 'MIT License',

install_requires = ['simplejson>=11'],

author = 'yjx',

author_email = 'not@allcom',

packages = find_packages(),

platforms = 'any',

)

然后将代码打包。

打包只需要执行python

setuppy xxx命令即可,其中xxx是打包格式的选项,如下:

# 以下所有生成文件将在当前路径下 dist 目录中

python setuppy bdist_egg # 生成easy_install支持的格式

python setuppy sdist # 生成pip支持的格式,下文以此为例

发布到pypi。

发布到pypi首先需要注册一个账号,然后进行如下两步:

注册package。输入python setuppy register。

上传文件。输入python setuppy sdist upload。

安装测试

上传成功后,就可以使用pip来下载安装了。

另外,pypi还有一个测试服务器,可以在这个测试服务器上做测试,测试的时候需要给命令指定额外的"-r"或"-i"选项,如python

setuppy register -r "",python

setuppy sdist upload -r "",pip

install -i "" simpletest。

发布到测试服务器的时候,建议在linux或cygwin中发布,如果是在windows中,参考文档,需要生成pypirc文件

socket服务器再细分可分为多种了,tcp,udp,websocket,都是调用socket模块,但是具体实现起来有一点细微的差别

先给出一个tcp和udp通过socket协议实现的聊天室的例子

python聊天室(python27版本):

都是分别运行serverpy和clientpy,就可以进行通讯了。

TCP版本:

socket-tcp-serverpy(服务端):

 

#-- encoding:utf-8 --

#socketgetaddrinfo(host,  port, family=0, socktype=0, proto=0, flags=0)

#根据给定的参数host/port,相应的转换成一个包含用于创建socket对象的五元组,

#参数host为域名,以字符串形式给出代表一个IPV4/IPV6地址或者None

#参数port如果字符串形式就代表一个服务名,比如“http”"ftp""email"等,或者为数字,或者为None

#参数family为地主族,可以为AF_INET  ,AF_INET6 ,AF_UNIX

#参数socktype可以为SOCK_STREAM(TCP)或者SOCK_DGRAM(UDP)

#参数proto通常为0可以直接忽略

#参数flags为AI_的组合,比如AI_NUMERICHOST,它会影响函数的返回值

#附注:给参数host,port传递None时建立在C基础,通过传递NULL。

#该函数返回一个五元组(family, socktype, proto, canonname, sockaddr),同时第五个参数sockaddr也是一个二元组(address, port)

#更多的方法及链接请访问

# Echo server program

from socket import 

import sys

import threading

from time import ctime

from time import localtime

import traceback

import time

import subprocess

reload(sys)

syssetdefaultencoding("utf8")

 

 

HOST='127001'

PORT=8555  #设置侦听端口

BUFSIZ=1024

 

class TcpServer():

    def __init__(self):

        selfADDR=(HOST, PORT)

        try:

            selfsock=socket(AF_INET, SOCK_STREAM)

            print '%d is open' % PORT

 

            selfsockbind(selfADDR)

            selfsocklisten(5)

            #设置退出条件

            selfSTOP_CHAT=False

 

            # 所有监听的客户端

            selfclients = {}

            selfthrs = {}

            selfstops = []

 

        except Exception,e:

            print "%d is down" % PORT

            return False

 

    def IsOpen(ip, port):

 

        s = socket(AF_INET, SOCK_STREAM)

        try:

            sconnect((ip, int(port)))

            # sshutdown(2)

            # 利用shutdown()函数使socket双向数据传输变为单向数据传输。shutdown()需要一个单独的参数,

            # 该参数表示s了如何关闭socket。具体为:0表示禁止将来读;1表示禁止将来写;2表示禁止将来读和写。

            print '%d is open' % port

            return True

        except:

            print '%d is down' % port

            return False

 

    def listen_client(self):

        while not selfSTOP_CHAT:

            print(u'等待接入,侦听端口:%d' % (PORT))

            selftcpClientSock, selfaddr=selfsockaccept()

            print(u'接受连接,客户端地址:',selfaddr)

            address = selfaddr

            #将建立的client socket链接放到列表selfclients中

            selfclients[address] = selftcpClientSock

            #分别将每个建立的链接放入进程中,接收且分发消息

            selfthrs[address] = threadingThread(target=selfreadmsg, args=[address])

            selfthrs[address]start()

            timesleep(05)

 

 

 

    def readmsg(self,address):

        #如果地址不存在,则返回False

        if address not in selfclients:

            return False

        #得到发送消息的client socket

        client = selfclients[address]

        while True:

            try:

                #获取到消息内容data

                data=clientrecv(BUFSIZ)

            except:

                print(e)

                selfclose_client(address)

                break

            if not data:

                break

            #python3使用bytes,所以要进行编码

            #s='%s发送给我的信息是:[%s] %s' %(addr[0],ctime(), datadecode('utf8'))

            #对日期进行一下格式化

            ISOTIMEFORMAT='%Y-%m-%d %X'

            stime=timestrftime(ISOTIMEFORMAT, localtime())

            s=u'%s发送给我的信息是:%s' %(str(address),datadecode('utf8'))

            #将获得的消息分发给链接中的client socket

            for k in selfclients:

                selfclients[k]send(sencode('utf8'))

                selfclients[k]sendall('sendall:'+sencode('utf8'))

                print str(k)

            print([stime], ':', datadecode('utf8'))

            #如果输入quit(忽略大小写),则程序退出

            STOP_CHAT=(datadecode('utf8')upper()=="QUIT")

            if STOP_CHAT:

                print "quit"

                selfclose_client(address)

                print "already quit"

                break

 

    def close_client(self,address):

        try:

            client = selfclientspop(address)

            selfstopsappend(address)

            clientclose()

            for k in selfclients:

                selfclients[k]send(str(address) + u"已经离开了")

        except:

            pass

        print(str(address)+u'已经退出')

 

 

if __name__ == '__main__':

    tserver = TcpServer()

    tserverlisten_client()

     

 ——————————华丽的分割线——————————       

     

 socket-tcp-clientpy (客户端):

  

#-- encoding:utf-8 --

from socket import 

import sys

import threading

import time

reload(sys)

syssetdefaultencoding("utf8")

 

 

#测试,连接本机

HOST='127001'

#设置侦听端口

PORT=8555

BUFSIZ=1024

 

class TcpClient:

 

    ADDR=(HOST, PORT)

    def __init__(self):

        selfHOST = HOST

        selfPORT = PORT

        selfBUFSIZ = BUFSIZ

        #创建socket连接

        selfclient = socket(AF_INET, SOCK_STREAM)

        selfclientconnect(selfADDR)

        #起一个线程,监听接收的信息

        selftrecv = threadingThread(target=selfrecvmsg)

        selftrecvstart()

 

    def sendmsg(self):

        #循环发送聊天消息,如果socket连接存在则一直循环,发送quit时关闭链接

        while selfclientconnect_ex(selfADDR):

            data=raw_input('>:')

            if not data:

                break

            selfclientsend(dataencode('utf8'))

            print(u'发送信息到%s:%s' %(selfHOST,data))

            if dataupper()=="QUIT":

                selfclientclose()

                print u"已关闭"

                break

    def recvmsg(self):

        #接收消息,如果链接一直存在,则持续监听接收消息

        try:

            while selfclientconnect_ex(selfADDR):

                data=selfclientrecv(selfBUFSIZ)

                print(u'从%s收到信息:%s' %(selfHOST,datadecode('utf8')))

        except Exception,e:

            print str(e)

 

if __name__ == '__main__':

    client=TcpClient()

    clientsendmsg()

UDP版本:

socket-udp-serverpy

 

# -- coding:utf8 --

 

import sys

import time

import traceback

import threading

reload(sys)

syssetdefaultencoding('utf-8')

 

import socket

import traceback

 

HOST = "127001"

PORT = 9555

CHECK_PERIOD = 20

CHECK_TIMEOUT = 15

 

class UdpServer(object):

    def __init__(self):

        selfclients = []

        selfbeats = {}

        selfADDR = (HOST,PORT)

        try:

            selfsock = socketsocket(socketAF_INET, socketSOCK_DGRAM)

            selfsockbind(selfADDR)       # 绑定同一个域名下的所有机器

            selfbeattrs = threadingThread(target=selfcheckheartbeat)

            selfbeattrsstart()

        except Exception,e:

            tracebackprint_exc()

            return False

 

    def listen_client(self):

        while True:

            timesleep(05)

            print "hohohohohoo"

            try:

                recvData,address = selfsockrecvfrom(2048)

                if not recvData:

                    selfclose_client(address)

                    break

                if address in selfclients:

                    senddata = u"%s发送给我的信息是:%s" %(str(address),recvDatadecode('utf8'))

                    if recvDataupper() == "QUIT":

                        selfclose_client(address)

                    if recvData == "HEARTBEAT":

                        selfheartbeat(address)

                        continue

                else:

                    selfclientsappend(address)

                    senddata = u"%s发送给我的信息是:%s" %(str(address),u'进入了聊天室')

                for c in selfclients:

                    try:

                        selfsocksendto(senddata,c)

                    except Exception,e:

                        print str(e)

                        selfclose_client(c)

            except Exception,e:

                # tracebackprint_exc()

                print str(e)

                pass

 

    def heartbeat(self,address):

        selfbeats[address] = timetime()

 

    def checkheartbeat(self):

 

        while True:

            print "checkheartbeat"

            print selfbeats

            try:

                for c in selfclients:

                    print timetime()

                    print selfbeats[c]

                    if selfbeats[c] + CHECK_TIMEOUT <timetime():

                        print u"%s心跳超时,连接已经断开" %str(c)

                        selfclose_client(c)

                    else:

                        print u"checkp%s,没有断开" %str(c)

            except Exception,e:

                tracebackprint_exc()

                print str(e)

                pass

            timesleep(CHECK_PERIOD)

 

    def close_client(self,address):

        try:

            if address in selfclients:

                selfclientsremove(address)

                if selfbeatshas_key(address):

                    del selfbeats[address]

                print selfclients

            for c in selfclients:

                selfsocksendto(u'%s已经离开了' % str(address),c)

            print(str(address)+u'已经退出')

        except Exception,e:

            print str(e)

            raise

 

if __name__ == "__main__":

    udpServer = UdpServer()

    udpServerlisten_client()

     

——————————华丽的分割线——————————    

socket-udp-clientpy:

# -- coding:utf8 --

 

import sys

import threading

import time

reload(sys)

syssetdefaultencoding('utf-8')

 

import socket

 

HOST = "127001"

PORT = 9555

#BEAT_PORT = 43278

BEAT_PERIOD = 5

 

 

class UdpClient(object):

    def __init__(self):

        selfclientsock = socketsocket(socketAF_INET,socketSOCK_DGRAM)

        selfHOST = HOST

        selfADDR = (HOST,PORT)

        selfclientsocksendto(u'请求建立链接',selfADDR)

        selfrecvtrs = threadingThread(target=selfrecvmsg)

        selfrecvtrsstart()

        selfhearttrs = threadingThread(target=selfheartbeat)

        selfhearttrsstart()

 

    def sendmsg(self):

        while True:

            data = raw_input(">:")

            if not data:

                break

            selfclientsocksendto(dataencode('utf-8'),selfADDR)

            if dataupper() == 'QUIT':

                selfclientsockclose()

                break

 

    def heartbeat(self):

        while True:

            selfclientsocksendto('HEARTBEAT',selfADDR)

            timesleep(BEAT_PERIOD)

 

    def recvmsg(self):

        while True:

            recvData,addr = selfclientsockrecvfrom(1024)

            if not recvData:

                break

            print(u'从%s收到信息:%s' %(selfHOST,recvDatadecode('utf8')))

 

 

 

if __name__ == "__main__":

    udpClient = UdpClient()

    udpClientsendmsg()

DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
网站模板库 » 如何用python和web.py搭建一个网站

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情