python--Mini_Web

Mini_Web

常用单词

  • request 英 [rɪ'kwest] 请求;需要
  • response 英 [rɪ'spɒns] 响应;反应;
  • route 英 [ruːt] 按某路线发送 ; 路由
  • template 英 ['templeɪt; -plɪt] 模板,样板
  • source 英 [sɔːs] 来源;水源;

Web应用概述


1. Web开发流程

前面已经学习过Web服务器, 我们知道Web服务器主要是接收用户的http请求,根据用户的请求返回不同的资源数据,但是之前我们开发的是静态Web服务器,返回的都是静态资源数据,假如我们想要Web服务器返回动态资源那么该如何进行处理呢?

 

 

关系说明:

  • Web服务器接收浏览器发起的请求,如果是动态资源请求找Web应用
  • Web应用负责处理浏览器的动态资源请求,把处理的结果发送给Web服务器
  • Web服务器再把响应结果发生给浏览器

2. 静态资源

不需要经常变化的资源,这种资源Web服务器可以提前准备好,比如: png/jpg/css/js等文件。

3. 动态资源

和静态资源相反, 这种资源会经常变化,比如: 我们在京东浏览商品时经常会根据条件进行筛选,选择不同条件, 浏览的商品就不同,这种资源Web服务器无法提前准备好,需要Web应用来帮Web服务器进行准备。

4. WSGI协议

它是Web服务器和Web应用之间进行协同工作的一个规则,WSGI协议规定Web服务器把动态资源的请求信息传给Web应用处理,Web应用把处理好的结果返回给Web服务器。

5. 小结

  • Web应用是专门为Web服务器处理动态资源请求的一个程序
  • Web应用和Web服务器的关系是Web应用专门服务于Web服务器,给Web服务器提供处理动态资源请求的服务。

 

Web应用开发


1. Web应用职责介绍

  • 接收web服务器的动态资源请求,给web服务器提供处理动态资源请求的服务。

2. 动态资源判断

  • 根据请求资源路径进行判断

web服务器程序(web.py)代码:

import socket
import threading
import dynamic.frame


# 获取用户请求资源的路径
# 根据请求资源的路径,读取指定文件的数据
# 组装指定文件数据的响应报文,发送给浏览器
# 判断请求的文件在服务端不存在,组装404状态的响应报文,发送给浏览器
class HttpWebServer:
    def __init__(self):
        # 1.编写一个TCP服务端程序
        # 创建socekt
        self.tcp_server_socekt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置端口复用 
        self.tcp_server_socekt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        # 绑定地址 ip地址替换为自己电脑的ip
        self.tcp_server_socekt.bind(("192.168.99.117", 8000))
        # 设置监听
        self.tcp_server_socekt.listen(128)

    def handle_client_request(self, client_socekt, client_addr):
        # 获取浏览器的请求信息
        client_request_data = client_socekt.recv(1024).decode()

        # 判断客户端是否关闭
        if len(client_request_data) == 0:
            print('客户端关闭')
            client_socekt.close()
            return

        # 获取用户请求信息
        request_data = client_request_data.split("\r\n")

        # 获取请求行数据
        request_line = request_data.pop(0)  # 使用pop将请求行数据从列表中取出
        request_line_data = request_line.split(' ')

        # 获取请求头部数据
        request_header = {}
        for data in request_data:
            if data == '':
                continue
            request_header[data.split(": ")[0]] = data.split(": ")[1]

        # 将客户端ip地址添加到header中
        request_header['client_addr'] = client_addr


        # 请求行中获取请求资源的路径
        request_path = request_line_data[1]

        # 判断是否有查询字符串数据
        request_query = request_path.split('?')
        if len(request_query) > 1:
            request_path = request_query[0]
            query_str = request_query[1]
        else:
            query_str = None

        # 构造请求数据
        requests = {
            'path': request_path,
            'query_str': query_str,
            'header': request_header,

        }

        """动态资源"""
        # 符合wsgi协议的参数
        env = {
            "request_path": request_path,
            "requests": requests
        }

        # 应答行
        response_line = "HTTP/1.1 200 OK\r\n"
        # 应答头
        response_header = "Server:pwb\r\nAccess-Control-Allow-Credentials:true\r\nAccess-Control-Allow-Origin:*\r\nAccess-Control-Allow-Methods:GET, POST, PUT\n\rAccess-Control-Allow-Headers:X-Custom-Header"
        # 应答体
        response_body = dynamic.frame.application(env)
        # 应答数据
        response_data = response_line + response_header + "\r\n\r\n" + response_body
        # 发送数据给到浏览器
        client_socekt.send(response_data.encode())

        # 关闭和浏览器通讯的socket
        client_socekt.close()

    def start(self):
        while True:
            # 2.获取浏览器发送的HTTP请求报文数据
            # 建立链接
            client_socekt, client_addr = self.tcp_server_socekt.accept()
            # 创建子线程
            sub_thread = threading.Thread(target=self.handle_client_request, args=(client_socekt, client_addr))
            sub_thread.start()


if __name__ == '__main__':
    # 创建服务器对象
    my_web_server = HttpWebServer()
    # 启动服务器
    my_web_server.start()Copy

3. 处理客户端的动态资源请求

  1. 创建web应用程序
  2. 接收web服务器的动态资源请求
  3. 处理web服务器的动态资源请求并把处理结果返回给web服务器
  4. web服务器把处理结果组装成响应报文发送给浏览器

web应用程序(framework.py)代码:

"""miniweb,负责处理动态资源请求"""
import re
from pymysql import *
import json


# ajax请求的数据,获取首页数据
def index(env):
    # 创建链接
    conn = connect(host='localhost', port=3306, database='booksite', user='root', password='mysql', charset='utf8')
    # 创建游标
    cursor = conn.cursor()

    # 执行sql语句
    sql = "select *  from bookinfo;"
    cursor.execute(sql)

    # 获取数据 元组  ((),())
    stock_data = cursor.fetchall()

    # 把元组转化成列表
    center_data_list =[]
    for data in stock_data:
        center_data_list.append({
            "id":data[0],
            "name":data[1],
            "auth":data[2],
            "img_url":data[3],
            "rank":data[4]
        })
     # 把列表转化成json字符串
    # ensure_ascii = False 控制台中可以显示中文
    json_str = json.dumps(center_data_list, ensure_ascii=False)


    # 关闭连接
    cursor.close()
    conn.close()

    return json_str



# 处理动态资源请求
def handle_request(env):
    # 获取动态请求资源路径
    request_path = env["request_path"]
    print("接收到的动态资源请求:", request_path)

    if request_path == "/index":
        # 获取首页数据
        result = index(env)
        return result
    else:
        # 没有找到动态资源
        return "404 not found..."Copy

4. 小结

  • 动态资源的判断通过请求资源路径来完成
  • 处理客户端的动态资源请求
    1. 接收web服务器的动态资源请求
    2. Web应用程序处理动态资源请求并把处理结果返回给web服务器
    3. web服务器把处理结果组装成响应报文发送给浏览器

路由列表功能开发


1. 路由的介绍

接着上面程序的判断场景,假如咱们再处理一个图书详情的动态资源请求非常简单,再添加一个函数和更加一个分支判断就可以实现了。

framework.py 示例代码:

# 获取首页数据
def index(env):
    # 创建链接
    conn = connect(host='localhost', port=3306, database='booksite', user='root', password='mysql', charset='utf8')
    # 创建游标
    cursor = conn.cursor()

    # 执行sql语句
    sql = "select *  from bookinfo;"
    cursor.execute(sql)

    # 获取数据 元组  ((),())
    stock_data = cursor.fetchall()

    # 把元组转化成列表
    center_data_list =[]
    for data in stock_data:
        center_data_list.append({
            "id":data[0],
            "name":data[1],
            "auth":data[2],
            "img_url":data[3],
            "rank":data[4]
        })
     # 把列表转化成json字符串
    # ensure_ascii = False 控制台中可以显示中文
    json_str = json.dumps(center_data_list, ensure_ascii=False)


    # 关闭连接
    cursor.close()
    conn.close()

    return json_str

# 获取详情页
def detail(env):
    # 获取前端传递数据
    data = env['requests']['query_str']
    header = env['requests']['header']
    # 创建链接
    conn = connect(host='localhost', port=3306, database='booksite', user='root', password='mysql', charset='utf8')
    # 创建游标
    cursor = conn.cursor()
    # 执行sql语句 按照指定条件查询
    sql = "select *  from bookinfo where %s;"%data
    cursor.execute(sql)

    # 获取数据 元组  ((),())
    stock_data = cursor.fetchone()
    data_dict = {
            "id":stock_data[0],
            "name":stock_data[1],
            "auth":stock_data[2],
            "img_url":stock_data[3],
            "read":stock_data[5],
            "comment":stock_data[6],
            "score":stock_data[8],
            "content":stock_data[7],
            "synopsis":stock_data[9]
        }

    # 将阅读量更新后写入数据库
    read = stock_data[5] + 1
    sql = 'update bookinfo set bread=%d where id=%d;'%(read,stock_data[0])
    cursor.execute(sql)
    conn.commit()

    json_str = json.dumps(data_dict, ensure_ascii=False)

    return json_str

# 处理动态资源请求
def handle_request(env):
    # 获取动态请求资源路径
    request_path = env["request_path"]
    print("接收到的动态资源请求:", request_path)

    if request_path == "/index":
        # 获取首页数d
        result = index()
        return result
    elif request_path == "/detail":
        # 获取详情页数据
        result = detail(env)
        return result
    else:
        # 没有找到动态资源
        return "404 not found..."Copy

那如果咱们的应用处理的页面请求路径再多一些,比如:5个路径判断,大家可能感觉条件分支完全可以胜任,如果是40个甚至更多呢? 如果这是还是用普通的条件分支简直无法忍受。

解决办法: 可以使用路由

什么是路由?

路由就是请求的URL到处理函数的映射,也就是说提前把请求的URL和处理函数关联好。

路由列表

这么多的路由如何管理呢, 可以使用一个路由列表进行管理,通过路由列表保存每一个路由。

请求路径处理函数
/loginlogin函数
/indexindex函数
/detaildetail函数

2. 在路由列表添加路由

framework.py 示例代码:

# 定义路由列表
route_list = [
    ("/index", index),
    ("/detail", detail)
]Copy

3. 根据用户请求遍历路由列表处理用户请求

framework.py 示例代码:


# 处理动态资源请求
def handle_request(env):
    # 获取动态请求资源路径
    request_path = env["request_path"]
    print("接收到的动态资源请求:", request_path)
    # 遍历路由列表,选择执行的函数
    for path, func in route_list:
        if request_path == path:
            result = func()
            return result
    else:
        # 没有找到动态资源
       return "404 not found..."

    # if request_path == "/index":
    #     # 获取首页数据
    #     result = index()
    #     return result
    # elif request_path == "/detail":
    #     # 获取个人中心数据
    #     result = detail()
    #     return result
    # else:
    #     # 没有找到动态资源
    #     return "404 not found..."Copy

小结

  • 路由是请求的URL到处理函数的映射
  • 路由列表是用来保存每一个设置好的路由
  • 用户的动态资源请求通过遍历路由列表找到对应的处理函数来完成。

logging日志


1. logging日志的介绍

在现实生活中,记录日志非常重要,比如:银行转账时会有转账记录;飞机飞行过程中,会有个黑盒子(飞行数据记录器)记录着飞机的飞行过程,那在咱们python程序中想要记录程序在运行时所产生的日志信息,怎么做呢?

可以使用 logging 这个包来完成

记录程序日志信息的目的是:

  1. 可以很方便的了解程序的运行情况
  2. 可以分析用户的操作行为、喜好等信息
  3. 方便开发人员检查bug

2. logging日志级别介绍

日志等级可以分为5个,从低到高分别是:

  1. DEBUG
  2. INFO
  3. WARNING
  4. ERROR
  5. CRITICAL

日志等级说明:

  • DEBUG:程序调试bug时使用
  • INFO:程序正常运行时使用
  • WARNING:程序未按预期运行时使用,但并不是错误,如:用户登录密码错误
  • ERROR:程序出错误时使用,如:IO操作失败
  • CRITICAL:特别严重的问题,导致程序不能再继续运行时使用,如:磁盘空间为空,一般很少使用
  • 默认的是WARNING等级,当在WARNING或WARNING之上等级的才记录日志信息。
  • 日志等级从低到高的顺序是: DEBUG < INFO < WARNING < ERROR < CRITICAL

3. logging日志的使用

在 logging 包中记录日志的方式有两种:

  1. 输出到控制台
  2. 保存到日志文件

日志信息输出到控制台的示例代码:

import logging

logging.debug('这是一个debug级别的日志信息')
logging.info('这是一个info级别的日志信息')
logging.warning('这是一个warning级别的日志信息')
logging.error('这是一个error级别的日志信息')
logging.critical('这是一个critical级别的日志信息')Copy

运行结果:

WARNING:root:这是一个warning级别的日志信息
ERROR:root:这是一个error级别的日志信息
CRITICAL:root:这是一个critical级别的日志信息Copy

说明:

  • 日志信息只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING

logging日志等级和输出格式的设置:

import logging

# 设置日志等级和输出日志格式
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')

logging.debug('这是一个debug级别的日志信息')
logging.info('这是一个info级别的日志信息')
logging.warning('这是一个warning级别的日志信息')
logging.error('这是一个error级别的日志信息')
logging.critical('这是一个critical级别的日志信息')Copy

运行结果:

2019-02-13 20:41:33,080 - hello.py[line:6] - DEBUG: 这是一个debug级别的日志信息
2019-02-13 20:41:33,080 - hello.py[line:7] - INFO: 这是一个info级别的日志信息
2019-02-13 20:41:33,080 - hello.py[line:8] - WARNING: 这是一个warning级别的日志信息
2019-02-13 20:41:33,080 - hello.py[line:9] - ERROR: 这是一个error级别的日志信息
2019-02-13 20:41:33,080 - hello.py[line:10] - CRITICAL: 这是一个critical级别的日志信息Copy

代码说明:

  • level 表示设置的日志等级
  • format 表示日志的输出格式, 参数说明:
    • %(levelname)s: 打印日志级别名称
    • %(filename)s: 打印当前执行程序名
    • %(lineno)d: 打印日志的当前行号
    • %(asctime)s: 打印日志的时间
    • %(message)s: 打印日志信息

日志信息保存到日志文件的示例代码:

import logging

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
                    filename="log.txt",
                    filemode="w")

logging.debug('这是一个debug级别的日志信息')
logging.info('这是一个info级别的日志信息')
logging.warning('这是一个warning级别的日志信息')
logging.error('这是一个error级别的日志信息')
logging.critical('这是一个critical级别的日志信息')Copy

运行结果:

4. logging日志在mini-web项目中应用

web.py 程序使用logging日志示例:

  1. 程序入口模块设置logging日志的设置

     import socket
     import threading
     import sys
     import framework
     import logging
    
     # logging日志的配置
     logging.basicConfig(level=logging.DEBUG,
                         format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
                         filename="log.txt",
                         filemode="w")Copy
  2. INFO级别的日志输出,示例代码:

     # 判断是否是动态资源请求
     if request_path.endswith(".html"):
         """这里是动态资源请求,把请求信息交给框架处理"""
         logging.info("动态资源请求:" + request_path)
         ...
     else:
         """这里是静态资源请求"""
         logging.info("静态资源请求:" + request_path)
         ...Copy
  3. WARNING级别的日志输出,示例代码:

     # 获取命令行参数判断长度
     if len(sys.argv) != 2:
         print("执行命令如下: python3 xxx.py 9000")
         logging.warning("用户在命令行启动程序参数个数不正确!")
         return
    
     # 判断端口号是否是数字
     if not sys.argv[1].isdigit():
         print("执行命令如下: python3 xxx.py 9000")
         logging.warning("用户在命令行启动程序参数不是数字字符串!")
         returnCopy

framework.py 程序使用logging日志示例:

  1. ERROR级别的日志输出,示例代码:

     # 处理动态资源请求
     def handle_request(env):
         # 获取动态请求资源路径
         request_path = env["request_path"]
         print("接收到的动态资源请求:", request_path)
         # 遍历路由列表,选择执行的函数
         for path, func in route_list:
             if request_path == path:
                 result = func()
                 return result
         else:
             logging.error("没有设置相应的路由:" + request_path)
            # 没有找到动态资源
            return "404 not foCopy

说明:

  • logging日志配置信息在程序入口模块设置一次,整个程序都可以生效。
    • logging.basicConfig 表示 logging 日志配置操作

5. 小结

  • 记录python程序中日志信息使用 logging 包来完成
  • logging日志等级有5个:

    1. DEBUG
    2. INFO
    3. WARNING
    4. ERROR
    5. CRITICAL
  • 打印(记录)日志的函数有5个:

    1. logging.debug函数, 表示: 打印(记录)DEBUG级别的日志信息
    2. logging.info函数, 表示: 打印(记录)INFO级别的日志信息
    3. logging.warning函数, 表示: 打印(记录)WARNING级别的日志信息
    4. logging.error函数, 表示: 打印(记录)ERROR级别的日志信息
    5. logging.critical函数, 表示: 打印(记录)CRITICAL级别的日志信息

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值