路漫漫其修远兮,吾将上下而求索。

导航

web实质

1 一个最简单版的web服务

web服务端是一个socket服务,浏览器是一个socket客户端,通过浏览器访问指定网页,其实就是一个socket客户端跟socket服务端的通信过程,代码如下:
# socket服务端,实现在浏览器页面上显示'hello python'
import socket

server = socket.socket()
server.bind(('127.0.0.1', 9001))
server.listen()

while True:
    conn, _ = server.accept()
    data = conn.recv(1024)
    print(data.decode('utf-8'))
    conn.send(b'HTTP/1.1 200 ok\r\n\r\nhello python') # http的返回格式+返回数据,可分开写
    # conn.send(b'HTTP/1.1 200 ok\r\n\r\n') # 将http格式和数据分开写,http格式内容
    # conn.send(b'hello python') # 将http格式和数据分开写,数据内容
    conn.close()
server.close()

此时,我们在浏览器中输入IP地址和端口(127.0.0.1:9001)就可以看到发送的socket服务端发送过来的数据'hello python'了,截图如下:

2 访问磁盘中文件的web服务

网页中显示的内容,一般是存储在文件中,下面我们展示如何把存储在服务器上的文件显示在浏览器页面中。
通过上面的最简化版的web服务我们可以知道,要实现将文件test.txt的信息展示在浏览器页面上,只需要socket服务端读取文件test.txt内容,然后将读取到的test.txt的内容发送到浏览器即可。

文件内容如下:

# 文件名:test.txt
God helps those who help themselves.

socket服务端代码如下:

import socket

server = socket.socket()
server.bind(('127.0.0.1', 9001))
server.listen()

while True:
    conn, _ = server.accept()
    data = conn.recv(1024)
    print(data.decode('utf-8'))
    conn.send(b'HTTP/1.1 200 ok\r\n\r\n')
    with open('test.txt', 'rb') as f:  # 读文件
        data = f.read()
    conn.send(data)     # 发送文件中的内容到浏览器
    conn.close()
server.close()

浏览器访问后,截图如下:

3. 在浏览器中展示html文件

接下来,我们将展示如何将html文件(html文件也是个文件,因此跟上面将普通文件显示在浏览器中毫无区别)显示在浏览器中
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 style="color:red">web文件</h1>
<p style="color:green">这是一个简单的html文件,将显示在浏览器中</p>
</body>
</html>

socket服务端代码如下:

import socket

server = socket.socket()
server.bind(('127.0.0.1', 9001))
server.listen()

while True:
    conn, _ = server.accept()
    data = conn.recv(1024)
    print(data.decode('utf-8'))
    conn.send(b'HTTP/1.1 200 ok\r\n\r\n')
    with open('index.html', 'rb') as f:  # 只需要将文件名修改成html文件即可
        data = f.read()
        
    conn.send(data)
    conn.close()
server.close()

运行代码后,打开浏览器,访问结果如下:

在上面的代码运行过程中,我们打印了浏览器请求的信息,截图如下:

4. 通过在浏览器中输入不同url,返回不同的页面内容

我们在使用浏览器访问服务器的时候,会看到不同得url路经,不同的url路经将显示不同的页面内容,下面我们将实验使用的一个test.txt文件index.html文件分别显示出来,由于将要显示不同路经下的两个文件,因此我们可以通过if判断进行区别,并将放回的页面信息封装到函数中
import  socket

server = socket.socket()
server.bind(('127.0.0.1', 9001))
server.listen()

def test(conn):
    with open('test.txt', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def index(conn):
    with open('index.html', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def err():
    err = b'404 not found!'
    conn.send(err)
    conn.close()

while True:
    conn, addr = server.accept()
    from_b_msg = conn.recv(1024)
    str_msg = from_b_msg.decode('utf-8')
    path = str_msg.split('\r\n')[0].split(' ')[1]
    # 将客户端发送的请求信息进行分割,获取到浏览器访问的文件路径,再通过路径判断将用户请求页面返回
    print('path>>>', path)
    conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
    print(from_b_msg)
    if path == '/index.html':
        index(conn)
    elif path == '/test.txt':
        test(conn)
    else:
        err()

访问html文件,结果如下:

访问txt文件,结果如下:

访问其他路径地址,则显示错误提示,结果如下:

5 多线程版的web框架:

import socket
from threading import Thread

server = socket.socket()
server.bind(('127.0.0.1', 9001))
server.listen()

def test(conn):
    with open('test.txt', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def index(conn):
    with open('index.html', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def err():
    err = b'404 not found!'
    conn.send(err)
    conn.close()

while True:
    conn, addr = server.accept()
    from_b_msg = conn.recv(1024)
    str_msg = from_b_msg.decode('utf-8')
    path = str_msg.split('\r\n')[0].split(' ')[1]
    print('path>>>', path)
    conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
    print(str_msg)
    if path == '/index.html':
        t = Thread(target=index, args=(conn,))
        t.start()
    elif path == '/test.txt':
        t = Thread(target=test, args=(conn,))
        t.start()
    else:
        err()
上面的代码虽然能实现功能,但是,如果有成百上千的文件,是否要写成百上千个if判断呢?因此代码还可以进行如下的优化
import socket
from threading import Thread

server = socket.socket()
server.bind(('127.0.0.1', 9001))
server.listen()


def test(conn):
    with open('test.txt', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def index(conn):
    with open('index.html', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

path_list = [
    ('/index.html', index),
    ('/test.txt', test)
]

def fun(path, conn):
    for url in path_list:
        if path == url[0]:
            t = Thread(target=url[1], args=(conn,))
            t.start()

while True:
    conn, _ = server.accept()
    from_b_msg = conn.recv(1024)
    str_msg = from_b_msg.decode('utf-8')
    path = str_msg.split('\r\n')[0].split(' ')[1]
    print('path>>>', path)
    conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
    print(str_msg)
    fun(path, conn)

经过这样的优化后,以后有新的文件,只需要将文件信息添加到path_list列表中了,不用再写if判断了。

6 展示动态web页面

该案例中,我们将在浏览器中显示当前时间,用户不断刷新浏览器,浏览器将自动更新当前时间
import socket, time
from threading import Thread

server = socket.socket()
server.bind(('127.0.0.1', 9001))
server.listen()

def test(conn):
    with open('test.txt', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def index(conn):
    time_flag = str(time.strftime("%Y-%m-%d %H:%M:%S"))  # 先将当前时间拿到
    with open('index.html', 'r', encoding="utf8") as f:
        data = f.read()
    data = data.replace('web文件', '北京实时时间')  # 将html的文件内容做替换
    data = data.replace('这是一个简单的html文件,将显示在浏览器中', time_flag).encode('utf-8')  # 将html的文件内容做替换,显示我们指定的内容
    conn.send(data)
    conn.close()

path_list = [
    ('/index.html', index),
    ('/test.txt', test)
]

def fun(path, conn):
    for url in path_list:
        if path == url[0]:
            t = Thread(target=url[1], args=(conn,))
            t.start()

while True:
    conn, _ = server.accept()
    from_b_msg = conn.recv(1024)
    str_msg = from_b_msg.decode('utf-8')
    path = str_msg.split('\r\n')[0].split(' ')[1]
    print('path>>>', path)
    conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
    print(str_msg)
    fun(path, conn)

运行代码后,刷新页面将显示当前实时时间

再次刷新,页面如下:

7 从数据库读取数据并返回到浏览器

接下来的案例,我们将展示,连接MySQL数据库,将MySQL数据库中的信息展示到浏览器页面上
首先连接到数据库,获取到test数据库userinfo表中的内容
# 连接数据库文件 connectDB.py
import pymysql

def show_db():
    db = pymysql.connect(host='127.0.0.1',
                     user='root',
                     password="2048",
                     database='test',
                     port=13306)

    cursor = db.cursor(pymysql.cursors.DictCursor)
    sql = 'select name,age from userinfo;'
    cursor.execute(sql)
    db_data = cursor.fetchall()  # 获取到test数据库中userinfo表中的name和age的数据
    cursor.close()
    db.close()
    content_list = []   # 定义一个列表,用于存放我们修改过后的表数据
    for row in db_data:  # 将userinfo表中的每一行内容取到
        tp = '<tr><td>%s</td><td>%s</td></tr>' % (row['name'], row['age'])  # 将userinfo表中的name和age字段替换成html指定的显示格式
        content_list.append(tp)
        content = ''.join(content_list)
    return content
新建一个用于用户访问的html文件模板,只定义表头信息(如果不定义表头的话,也可以使用特定字符串,再socket服务端将这些字符串替换成将要显示的表头信息即可,这里为了容易理解,因此将表头信息定义出来),表数据信息用特殊字符串4个'@'代替,便于socket客户端替换
# 浏览器访问的页面文件index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Title</title>
</head>
<body>
    <table border="1">
        <thead>
            <tr>
                <td>姓名</td>
                <td>年龄</td>
            </tr>
        </thead>
        <tbody>
                @@@@
        </tbody>
    </table>
</body>
</html>

socket客户端需要做三件事:
                        - 连接到数据库,拿到数据库中userinfo表的所有信息
                        - 用数据库中表的信息替换掉html模板中的信息
                        - 将替换掉的数据返回给浏览器
# socket服务端
import socket
from threading import Thread
from connectDB import show_db

server = socket.socket()
server.bind(('127.0.0.1', 9001))
server.listen()

def test(conn):
    with open('test.txt', 'rb') as f:
        data = f.read()
    conn.send(data)
    conn.close()

def index(conn):
    userinfo_data = show_db()
    with open('db.html', 'r', encoding="utf8") as f:
        data = f.read()
    data = data.replace('@@@@', userinfo_data).encode('utf-8')
    conn.send(data)
    conn.close()

path_list = [
    ('/index.html', index),
    ('/test.txt', test)
]

def fun(path, conn):
    for url in path_list:
        if path == url[0]:
            t = Thread(target=url[1], args=(conn,))
            t.start()

while True:
    conn, _ = server.accept()
    from_b_msg = conn.recv(1024)
    str_msg = from_b_msg.decode('utf-8')
    path = str_msg.split('\r\n')[0].split(' ')[1]
    print('path>>>', path)
    conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
    print(str_msg)
    fun(path, conn)

访问浏览器,截图如下:

修改数据库数据数据

重新访问浏览器

8 使用jinja2模板渲染的web框架案例

上文中从数据库中取出数据,然后通过字符串替换,再显示到浏览器页面上,字符串替换的过程可以使用现成的模块jinja2来完成,代码如下。
from wsgiref.simple_server import make_server
from jinja2 import Template
import pymysql

def show_db():
    db = pymysql.connect(host='127.0.0.1',
                     user='root',
                     password="2048",
                     database='test',
                     port=13306)

    cursor = db.cursor(pymysql.cursors.DictCursor)
    sql = 'select name,age from userinfo;'
    cursor.execute(sql)
    db_data = cursor.fetchall()
    cursor.close()
    db.close()
    return db_data

def index():
    userinfo_data = show_db()
    print(userinfo_data)
    with open('JINJA2.html', 'r', encoding="utf-8") as f:
        data = f.read()
    template = Template(data)
    print(template)
    data = template.render({'user_list': userinfo_data})
    return [bytes(data, encoding="utf8"), ]

path_list = [
    ('/index.html', index),
    ]

def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    url = environ['PATH_INFO']
    func = None
    for item in path_list:
        if item[0] == url:
            func = item[1]
            break
    if func:
        return func()
    else:
        return [bytes("404 not found", encoding="utf8"), ]


if __name__ == '__main__':
    httpd = make_server('', 9001, run_server)
    print("Serving HTTP on port 9001...")
    httpd.serve_forever()

html模板文件如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <table border="1">
        <thead>
            <tr>
                <th>name</th>
                <th>age</th>
            </tr>
        </thead>
        <tbody>
            {% for i in user_list %}
            <tr>
                <td>{{ i['name'] }}</td>
                <td>{{ i['age'] }}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
</body>
</html>

浏览器访问后,显示内容如下:

posted on 2020-05-02 01:31  rushiy  阅读(251)  评论(0编辑  收藏  举报