Python之简易Web框架搭建

  • Web框架介绍
    • WSGI协议
  • Web框架开发
    • 项目结构
    • MyWebServer.py
      • 之前的静态服务器代码
      • WSGI协议的要求
      • 更新代码
    • framework.py
      • 返回时间
      • 模板文件
      • 用数据替换模板文件变量
      • 路由列表
      • 路由装饰器
      • 利用数据库显示股票信息
      • 框架为客户端提供个人数据接口
      • Ajax获取个人中心接口并渲染页面
      • 日志logging
        • 记录程序日志信息的目的
        • logging日志的等级
        • logging包的使用
        • 设置日志格式
        • 设置日志文件
        • 为服务器添加日志

Web框架介绍

我们已经知道web服务器主要的作用是,接收用户的http请求,根据用户的请求返回不同的资源数据。但是之前我们学习的都是静态web服务器,返回的都是静态资源数据

静态资源:不需要经常变化的资源,这种资源web服务器可以提前准备好,如: html页面模板、png、jpg、css、js等文件。)

动态资源:html页面模板+数据

为了让服务器返回动态html页面文件,我们需要一个Web框架

web框架专门负责处理用户的动态资源请求,根据数据库更新页面数据,将模板+数据组合成动态网页返回。

Web框架其实就是一个为web服务器提供服务的应用程序。

WSGI协议

WSGI全称Web Server Gateway Interface(Web服务器网关接口),或Python Web Server Gateway Interface

它是web服务器和web框架(或Python应用程序)之间进行协同工作的一个接口协议。

WSGI协议规定,web服务器需把动态资源的http请求报文转化WSGI协议格式,再传给web框架进行处理。

web框架处理好请求之后,再把html文件与数据进行组装,返回给web服务器。

web服务器得到动态页面的结果后,再封装http响应报文,返回给浏览器。


Web框架开发

项目结构

  • static内含css,fonts,image,js,html等静态文件
  • MyWebServer.py提供服务器程序
  • framework.py提供web框架程序
  • template内提供模板html文件

MyWebServer.py

之前的静态服务器代码
import socket
import threadingclass MyWebServer(object):def __init__(self, port):"""初始化:创建套接字"""self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)self.server_socket.bind(("localhost", port))self.server_socket.listen(128)def start(self):"""启动:建立连接,并开启子线程"""while True:new_socket, address = self.server_socket.accept()print("已连接", address, sep=" from ")sub_thread = threading.Thread(target=self.handle, args=(new_socket,), daemon=True)sub_thread.start()@staticmethoddef handle(handle_socket):"""利用子线程收发http格式数据"""request_data = handle_socket.recv(4096)if len(request_data) == 0:print("浏览器已断开连接...")handle_socket.close()returnrequest_content = request_data.decode("utf-8")print(request_content)# 以\r\n分割各项信息request_list = request_content.split("\r\n")# 提取请求的资源路径request_line = request_list[0]request_line_list = request_line.split(" ")request_method, request_path, request_version = request_line_list# 首页if request_path == "/":request_path = "/index.html"# 响应行与响应头信息置空response_line = response_header = ""# 根据请求路径准备好响应行和响应体try:with open("." + request_path, "rb") as request_file:response_body = request_file.read()except (FileExistsError, FileNotFoundError):response_line += f"{request_version} 404 Not Found\r\n"with open("./error.html", "rb") as request_file:response_body = request_file.read()else:response_line += f"{request_version} 200 OK\r\n"finally:# 准备好响应头信息response_header += "Server: MyWebServer2.0\r\n"# 向浏览器发送响应报文response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_bodyhandle_socket.send(response_data)# 断开与浏览器的连接handle_socket.close()def main():port = 8888my_web_server = MyWebServer(port)my_web_server.start()if __name__ == "__main__":main()
WSGI协议的要求

如果请求的资源是html文件,我们就可以认为这是动态资源请求。需要把这个请求封装成WSGI协议要求的格式,并传给web框架处理。

WSGI协议要求服务器将请求报文的各项信息组合成一个字典environ,再传给web框架

environ={"request-path":"..."  #请求路径#...(以键值对形式传传入其他请求头信息)
}

为了追求简单,我们就这里就只传请求路径

更新代码

暂时先忽略framework的实现,我们假设在framework.py中,有一个Framework类。

更新服务器的初始化代码

from framework import FrameWorkdef __init__(self, port):"""初始化:创建套接字;初始化web框架"""self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)self.server_socket.bind(("localhost", port))self.server_socket.listen(128)self.framework=Framework()

self.framework负责处理动态资源请求(主要是html)。

如果请求的是html文件,就将请求信息分解为environ字典,并传给self.frameworkhandle_request处理。

处理完成后,web框架需要返回响应状态(status),响应头信息(headers,元组列表形式),响应体数据(response_body)。

服务器再根据这些信息拼接成http响应报文返回给浏览器。

如果请求的不是html文件,就让服务器行使其静态功能。

     #........        if request_path == "/":request_path = "/index.html"# 动态资源html交给web框架处理if request_path.endswith(".html"):environ = dict()environ["request_path"] = request_pathstatus, headers, response_body = self.framework.handle_request(environ)response_line = f"{request_version} {status}\r\n"response_header = ""for header in headers:response_header += "%s: %s\r\n" % headerresponse_data = (response_line + response_header + "\r\n" + response_body).encode("utf-8")handle_socket.send(response_data)# 断开与浏览器的连接handle_socket.close()# 静态资源静态web服务器处理else:response_line = response_header = "".......

framework.py

前面我们看到,framework的主要功能就是接受environ请求字典,返回对应响应状态(status),响应头信息(headers,元组形式),响应体数据(response_body)。

返回时间

第一步,为了寻求简单,我们就让请求index.html时返回现在的时间,请求其他网页时404 not Found

import timeclass FrameWork(object):@staticmethoddef __index():"""主页处理函数"""# 响应状态status = "200 OK"# 响应头信息response_header = [("Server", "MyWebServer2.0")]# 处理后的数据response_data = time.ctime()return status, response_header, response_data@staticmethoddef __not_found():"""未找到资源处理函数"""# 响应状态status = "404 Not Found"# 响应头response_header = [("Server", "PWS2.0")]# 处理后的数据response_data = "not found"return status, response_header, response_datadef handle_request(self, environ):"""framework框架主处理函数"""# 获取动态请求资源路径request_path = environ["request_path"]if request_path == "/index.html":# 获取首页数据result = self.__index()return resultelse:# 没有找到动态资源result = self.__not_found()return result

模板文件

动态html=静态html(模板)+数据库数据

在html中,我们可以用{%content%}这种形式的变量来标识需要动态替换的数据。这样的html,就可以称为模板。

如:

<!--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>首页 - 个人选股系统 V5.87</title><link href="/css/bootstrap.min.css" rel="stylesheet"><script src="/js/jquery-1.12.4.min.js"></script><script src="/js/bootstrap.min.js"></script><script>$(document).ready(function(){$("input[name='toAdd']").each(function(){  var currentAdd = $(this);  currentAdd.click(function(){  code = $(this).attr("systemIdVaule"); alert("/add/" + code + ".html"); $.get("/add/" + code + ".html", function(data, status){alert("数据: " + data + "\n状态: " + status);});});  });  });</script>
</head><body>
<div class="navbar navbar-inverse navbar-static-top "><div class="container"><div class="navbar-header"><button class="navbar-toggle" data-toggle="collapse" data-target="#mymenu"><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a href="#" class="navbar-brand">选股系统</a></div><div class="collapse navbar-collapse" id="mymenu"><ul class="nav navbar-nav"><li class="active"><a href="">股票信息</a></li><li><a href="/center.html">个人中心</a></li></ul></div></div>
</div>
<div class="container"><div class="container-fluid"><table class="table table-hover"><tr><th>序号</th><th>股票代码</th><th>股票简称</th><th>涨跌幅</th><th>换手率</th><th>最新价(元)</th><th>前期高点</th><th>前期高点日期</th><th>添加自选</th></tr>{%content%}</table></div>
</div>
</body>
</html>            
用数据替换模板文件变量

我们可以用数据库中的数据更换html中的{%content%}变量。

为了简单,这里直接用时间代替数据库数据来试试效果。

@staticmethoddef __index():"""主页处理函数"""# 响应状态status = "200 OK"# 响应头信息response_header = [("Server", "MyWebServer2.0")]# 处理后的数据with open("../template/index.html", "r",encoding="utf-8") as file:file_data = file.read()time_data = time.ctime()response_data = file_data.replace("{%content%}", time_data)return status, response_header, response_data

路由列表

目前我们只有处理请求index.html的函数。为了处理请求center.html的函数,我们可以再写一个center函数。

 @staticmethoddef __center():"""个人中心处理函数"""# 响应状态status = "200 OK"# 响应头信息response_header = [("Server", "MyWebServer2.0")]# 处理后的数据with open("../template/center.html", "r", encoding="utf-8") as file:file_data = file.read()time_data = time.ctime()response_data = file_data.replace("{%content%}", time_data)return status, response_header, response_data
    def handle_request(self, environ):"""framework框架主处理函数"""# 获取动态请求资源路径request_path = environ["request_path"]if request_path == "/index.html":# 获取首页数据result = self.__index()return resultelif request_path == "/center.html":# 获取首页数据result = self.__center()return resultelse:# 没有找到动态资源result = self.__not_found()return result

但这样的话,handle_request的代码未免太冗杂了。尤其是页面很多的情况下。

我们可以创建一个路由列表,它由二元组组成,二元组代表(页面请求路径,对应处理函数)的映射。

 def __init__(self):self.route_list=[("/index.html",self.__index),("/center.html",self.__center)]
    def handle_request(environ):"""framework框架主处理函数"""request_path = environ["request_path"]# 遍历路由列表,选择执行的函数for path, func in self.route_list:if request_path == path:result = func()return resultelse:# 没有找到动态资源result = self.__not_found()return result

这样的话,代码就简洁多了,再多页面也是一样。

我们只需创建处理不同页面请求的函数,并将请求路径与处理函数添加到路由列表中即可。

路由装饰器

通过含参装饰器的相关知识,我们可以实现,在定义处理函数的时候,自动向路由列表内添加二元映射。

route_list=[]def route(request_path):def decorator(func):route_list.append((request_path, func))  #这里在@时自动执行def inner(func, *args, **kwargs):result = func(*args, **kwargs)return resultreturn innerreturn decorator
class FrameWork(object):def __init__(self):self.route_list = route_list    #由于是可变对象,self.route_list和全局变量route_list保持一致
    @staticmethod@route("/index.html")   #自动添加映射("./index.html",Framework.__index)def __index():"""主页处理函数"""#...@staticmethod@route("/center")def __center():"""主页处理函数"""#...
利用数据库显示股票信息
create database gupiao;
use database gupiao;CREATE TABLE `info` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,`code` varchar(6) NOT NULL COMMENT '股票代码',`short` varchar(10) NOT NULL COMMENT '股票简称',`chg` varchar(10) NOT NULL COMMENT '涨跌幅',`turnover` varchar(255) NOT NULL COMMENT '换手率',`price` decimal(10,2) NOT NULL COMMENT '最新价',`highs` decimal(10,2) NOT NULL COMMENT '前期高点',`time` date DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=95 DEFAULT CHARSET=utf8;INSERT INTO `info` VALUES (1,'000007','全新好','10.01%','4.40%',16.05,14.60,'2017-07-18'),(2,'000036','华联控股','10.04%','10.80%',11.29,10.26,'2017-07-20'),(3,'000039','中集集团','1.35%','1.78%',18.07,18.06,'2017-06-28'),(4,'000050','深天马A','4.38%','4.65%',22.86,22.02,'2017-07-19'),(5,'000056','皇庭国际','0.39%','0.65%',12.96,12.91,'2017-07-20'),(6,'000059','华锦股份','3.37%','7.16%',12.26,12.24,'2017-04-11'),(7,'000060','中金岭南','1.34%','3.39%',12.08,11.92,'2017-07-20'),(8,'000426','兴业矿业','0.41%','2.17%',9.71,9.67,'2017-07-20'),(9,'000488','晨鸣纸业','6.30%','5.50%',16.37,15.59,'2017-07-10'),(10,'000528','柳工','1.84%','3.03%',9.42,9.33,'2017-07-19'),(11,'000540','中天金融','0.37%','5.46%',8.11,8.08,'2017-07-20'),(12,'000581','威孚高科','3.49%','3.72%',27.00,26.86,'2017-06-26'),(13,'000627','天茂集团','5.81%','12.51%',10.93,10.33,'2017-07-20'),(14,'000683','远兴能源','6.42%','21.27%',3.48,3.29,'2017-07-19'),(15,'000703','恒逸石化','0.24%','1.65%',16.92,16.88,'2017-07-20'),(16,'000822','山东海化','6.60%','8.54%',9.05,8.75,'2017-07-06'),(17,'000830','鲁西化工','1.38%','4.80%',7.36,7.26,'2017-07-20'),(18,'000878','云南铜业','1.26%','3.23%',14.50,14.47,'2017-07-19'),(19,'000905','厦门港务','5.44%','10.85%',15.90,15.60,'2017-04-20'),(20,'000990','诚志股份','0.53%','1.00%',16.99,16.90,'2017-07-20'),(21,'002019','亿帆医药','1.19%','2.81%',17.05,16.85,'2017-07-20'),(22,'002078','太阳纸业','2.05%','1.90%',8.45,8.29,'2017-07-19'),(23,'002092','中泰化学','7.25%','6.20%',15.53,14.48,'2017-07-20'),(24,'002145','中核钛白','2.43%','7.68%',6.75,6.61,'2017-07-19'),(25,'002285','世联行','8.59%','5.66%',9.23,8.50,'2017-07-20'),(26,'002311','海大集团','1.13%','0.24%',18.81,18.63,'2017-07-19'),(27,'002460','赣锋锂业','9.41%','9.00%',63.70,58.22,'2017-07-20'),(28,'002466','天齐锂业','3.62%','3.66%',68.44,66.05,'2017-07-20'),(29,'002470','金正大','2.30%','0.99%',8.00,7.82,'2017-07-20'),(30,'002496','辉丰股份','3.15%','4.29%',5.24,5.08,'2017-04-10'),(31,'002497','雅化集团','0.38%','12.36%',13.10,13.05,'2017-07-20'),(32,'002500','山西证券','0.44%','3.70%',11.49,11.44,'2017-07-20'),(33,'002636','金安国纪','2.70%','11.59%',19.80,19.42,'2017-07-19'),(34,'300032','金龙机电','0.66%','0.72%',15.28,15.18,'2017-07-20'),(35,'300115','长盈精密','0.60%','0.59%',33.50,33.41,'2017-07-19'),(36,'300268','万福生科','-10.00%','0.27%',31.77,13.57,'2017-04-10'),(37,'300280','南通锻压','3.31%','0.66%',32.20,32.00,'2017-04-11'),(38,'300320','海达股份','0.28%','0.82%',18.26,18.21,'2017-07-20'),(39,'300408','三环集团','1.69%','0.81%',23.42,23.17,'2017-07-19'),(40,'300477','合纵科技','2.84%','5.12%',22.10,22.00,'2017-07-12'),(41,'600020','中原高速','5.46%','4.48%',5.60,5.31,'2017-07-20'),(42,'600033','福建高速','1.01%','1.77%',4.00,3.99,'2017-06-26'),(43,'600066','宇通客车','4.15%','1.49%',23.08,23.05,'2017-06-13'),(44,'600067','冠城大通','0.40%','2.97%',7.56,7.53,'2017-07-20'),(45,'600110','诺德股份','2.08%','4.26%',16.16,15.83,'2017-07-20'),(46,'600133','东湖高新','9.65%','21.74%',13.64,12.44,'2017-07-20'),(47,'600153','建发股份','3.65%','2.03%',13.35,13.21,'2017-07-10'),(48,'600180','瑞茂通','2.20%','1.07%',14.86,14.54,'2017-07-20'),(49,'600183','生益科技','6.94%','4.06%',14.94,14.12,'2017-07-19'),(50,'600188','兖州煤业','1.53%','0.99%',14.56,14.43,'2017-07-19'),(51,'600191','华资实业','10.03%','11.72%',15.80,14.36,'2017-07-20'),(52,'600210','紫江企业','6.03%','10.90%',6.68,6.30,'2017-07-20'),(53,'600212','江泉实业','1.39%','1.78%',10.20,10.15,'2017-07-19'),(54,'600225','*ST松江','4.96%','2.47%',5.71,5.61,'2017-04-13'),(55,'600230','沧州大化','5.74%','13.54%',43.26,40.91,'2017-07-20'),(56,'600231','凌钢股份','2.79%','3.77%',3.68,3.60,'2017-07-19'),(57,'600291','西水股份','10.02%','9.23%',34.71,31.55,'2017-07-20'),(58,'600295','鄂尔多斯','4.96%','12.62%',16.51,15.73,'2017-07-20'),(59,'600303','曙光股份','8.37%','14.53%',11.53,10.64,'2017-07-20'),(60,'600308','华泰股份','1.12%','2.66%',6.30,6.26,'2017-07-19'),(61,'600309','万华化学','0.03%','1.78%',31.81,31.80,'2017-07-20'),(62,'600352','浙江龙盛','0.39%','1.85%',10.32,10.28,'2017-07-20'),(63,'600354','敦煌种业','7.89%','18.74%',9.44,8.75,'2017-07-20'),(64,'600408','安泰集团','1.98%','3.38%',4.13,4.12,'2017-04-13'),(65,'600409','三友化工','0.62%','3.78%',11.36,11.29,'2017-07-20'),(66,'600499','科达洁能','0.46%','3.94%',8.84,8.80,'2017-07-20'),(67,'600508','上海能源','3.26%','2.99%',13.32,13.01,'2017-07-19'),(68,'600563','法拉电子','0.32%','1.36%',53.67,53.50,'2017-07-20'),(69,'600567','山鹰纸业','0.76%','2.85%',3.98,3.96,'2017-07-19'),(70,'600585','海螺水泥','0.45%','0.61%',24.51,24.44,'2017-07-19'),(71,'600668','尖峰集团','4.35%','6.43%',18.70,18.36,'2017-04-13'),(72,'600688','上海石化','2.72%','0.91%',6.80,6.74,'2017-06-01'),(73,'600729','重庆百货','5.70%','3.34%',27.45,27.13,'2017-06-29'),(74,'600739','辽宁成大','3.30%','3.50%',19.74,19.11,'2017-07-20'),(75,'600779','水井坊','3.85%','2.77%',29.39,28.30,'2017-07-20'),(76,'600781','辅仁药业','8.61%','4.16%',23.46,21.89,'2017-05-02'),(77,'600801','华新水泥','4.00%','10.15%',12.99,12.49,'2017-07-20'),(78,'600846','同济科技','2.06%','17.41%',9.39,9.26,'2017-04-13'),(79,'600884','杉杉股份','1.08%','3.53%',20.67,20.45,'2017-07-20'),(80,'600966','博汇纸业','2.89%','5.54%',6.41,6.28,'2017-07-19'),(81,'600971','恒源煤电','2.36%','8.81%',12.16,11.88,'2017-07-20'),(82,'601012','隆基股份','0.76%','1.30%',19.93,19.78,'2017-07-20'),(83,'601100','恒立液压','4.78%','0.92%',19.31,18.97,'2017-07-13'),(84,'601101','昊华能源','4.03%','6.06%',11.10,10.80,'2017-07-19'),(85,'601216','君正集团','2.16%','2.26%',5.20,5.10,'2017-04-17'),(86,'601666','平煤股份','2.81%','6.14%',6.96,6.77,'2017-07-20'),(87,'601668','中国建筑','2.39%','1.42%',10.70,10.45,'2017-07-20'),(88,'601678','滨化股份','0.13%','2.47%',7.92,7.91,'2017-07-20'),(89,'601918','新集能源','1.23%','3.11%',4.93,4.92,'2017-07-19'),(90,'603167','渤海轮渡','2.77%','3.34%',11.87,11.61,'2017-04-13'),(91,'603369','今世缘','3.34%','2.13%',14.24,13.78,'2017-07-20'),(92,'603589','口子窖','3.99%','1.84%',39.37,39.04,'2017-06-26'),(93,'603799','华友钴业','2.38%','7.19%',67.46,65.89,'2017-07-20'),(94,'603993','洛阳钼业','2.94%','2.50%',7.36,7.16,'2017-07-19');CREATE TABLE `focus` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,`note_info` varchar(200) DEFAULT '',`info_id` int(10) unsigned DEFAULT NULL,PRIMARY KEY (`id`),KEY `info_id` (`info_id`),CONSTRAINT `focus_ibfk_1` FOREIGN KEY (`info_id`) REFERENCES `info` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8INSERT INTO `focus` VALUES (2,'你确定要买这个?',36),(3,'利好',37),(9,'',88),(10,'',89),(13,'',1);

以上sql语句建立了gupiao数据库和info,focus表

我们可以利用python的pymysql模块,查询数据库并用查询结果替换{%content%}

import pymysql#......@staticmethod@route("/index.html")def __index():"""主页处理函数"""# 响应状态status = "200 OK"# 响应头信息response_header = [("Server", "MyWebServer2.0")]# 处理后的数据with open("../template/index.html", "r", encoding="utf-8") as file:file_data = file.read()db_connect = pymysql.connect(host="localhost",port=3306,user="root",password="123",database="gupiao",charset="utf8")cursor = db_connect.cursor()sql = "select * from info;"cursor.execute(sql)result = cursor.fetchall()data = ""for row in result:data += '''<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td><input type="button" value="添加" id="toAdd" name="toAdd" systemidvaule="000007">                          </td></tr>''' % row# 替换模板文件中的模板变量response_data = file_data.replace("{%content%}", data)return status, response_header, response_data

框架为客户端提供个人数据接口

网页开发向来注重前后端分离。

后端提供数据支持,前端负责显示数据。

web框架程序可以开发数据接口,为浏览器提供数据服务。

数据接口一般是json文件

当请求路径是/center_data.html时,返回一个json字符串,为浏览器提供数据接口。

浏览器获得数据接口后,可利用Ajax进行局部刷新。

 @staticmethod@route("/center_data.html")def center_data():"""个人中心数据接口开发"""status = "200 OK"# 响应头response_header = [("Server", "MyWebServer2.0"), ("Content-Type", "text/html;charset=utf-8")]db_connect = pymysql.connect(host="localhost",port=3306,user="root",password="123",database="gupiao",charset="utf8")cursor = db_connect.cursor()sql = '''select info.code, info.short, info.chg, info.turnover,info.price,info.highs, info.note_info from info inner join focuson info.id = focus.info_id;'''cursor.execute(sql)result = cursor.fetchall()cursor.close()db_connect.close()# 个人中心数据列表center_data_list = list()# 遍历每一行数据转成字典for row in result:center_dict = dict()center_dict["code"] = row[0]center_dict["short"] = row[1]center_dict["chg"] = row[2]center_dict["turnover"] = row[3]center_dict["price"] = str(row[4])center_dict["highs"] = str(row[5])center_dict["note_info"] = row[6]center_data_list.append(center_dict)# 把列表字典转成json字符串json_str = json.dumps(center_data_list, ensure_ascii=False)return status, response_header, json_str

Ajax获取个人中心接口并渲染页面

一开始我们能够获取到center.html,但这个页面中没有数据。

为了获取数据,我们可以在center.html中加入Ajax代码,让浏览器去请求center_data.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>个人中心 - 个人选股系统 V5.87</title><link href="/css/bootstrap.min.css" rel="stylesheet"><script src="/js/jquery-1.12.4.min.js"></script><script src="/js/bootstrap.min.js"></script><script>$(document).ready(function(){// 发送ajax请求,获取个人中心数据$.get("center_data.html",function (center_data) {// 获取table标签var $table = $(".table");// 如果指定了返回数据的解析方式是json,那么data就是一个js对象for(var i = 0; i < center_data.length; i++){//获取每一行的js对象var $row = center_data[i];// 准备每一行数据var $rowStr = '<tr>' +'<td>'+ $row.code +'</td>' +'<td>'+ $row.short +'</td>' +'<td>'+ $row.chg +'</td>' +'<td>'+ $row.turnover +'</td>' +'<td>'+ $row.price +'</td>' +'<td>'+ $row.highs +'</td>' +'<td>'+ $row.note_info +'</td>' +'<td><a type="button" class="btn btn-default btn-xs" href="/update/000007.html"> <span class="glyphicon glyphicon-star" aria-hidden="true"></span> 修改 </a></td>' +'<td><input type="button" value="删除" id="toDel" name="toDel" systemidvaule="000007"></td>' +'</tr>'//table元素内追加数据$table.append($rowStr)}}, "json");$("input[name='toDel']").each(function(){var currentAdd = $(this);currentAdd.click(function(){code = $(this).attr("systemIdVaule");alert("/del/" + code + ".html");$.get("/del/" + code + ".html", function(data, status){alert("数据: " + data + "\n状态: " + status);});window.location.reload()});});});</script>
</head><body>
<div class="navbar navbar-inverse navbar-static-top "><div class="container"><div class="navbar-header"><button class="navbar-toggle" data-toggle="collapse" data-target="#mymenu"><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a href="#" class="navbar-brand">选股系统</a></div><div class="collapse navbar-collapse" id="mymenu"><ul class="nav navbar-nav"><li ><a href="./index.html">股票信息</a></li><li class="active"><a href="./center.html">个人中心</a></li></ul></div></div>
</div>
<div class="container"><div class="container-fluid"><table class="table table-hover"><tr><th>股票代码</th><th>股票简称</th><th>涨跌幅</th><th>换手率</th><th>最新价(元)</th><th>前期高点</th><th style="color:red">备注信息</th><th>修改备注</th><th>del</th></tr>{%content%}</table></div>
</div>
</body>
</html>            
日志logging

日志信息可以记录服务器的运行状态和信息。

记录程序日志信息的目的
  1. 可以很方便的了解程序的运行情况
  2. 可以分析用户的操作行为、喜好等信息
  3. 方便开发人员检查bug,调试程序
logging日志的等级

以下等级依次提高

  • DEBUG:程序调试bug时使用
  • INFO:程序正常运行时使用
  • WARNING:程序未按预期运行时使用,但并不是出现错误,如:用户登录密码错误
  • ERROR:程序出现错误时使用
  • CRITICAL:特别严重的问题,导致程序不能再继续运行时使用,如:磁盘空间为空,一般很少使用
logging包的使用
import logginglogging.debug('调试中...')
logging.info('正常运行中...')
logging.warning('警告!')
logging.error('出现错误,继续运行...')
logging.critical('出现错误,程序奔溃!')"""
WARNING:root:警告!
ERROR:root:出现错误,继续运行...
CRITICAL:root:出现错误,程序奔溃!
"""
  • 默认的日志等级为WARNING.日志信息只显示了大于等于日志等级的日志.
设置日志格式
logging.basicConfig(level=logging.DEBUG,format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
  • level是日志等级
  • format是日志格式
  • %(levelname)s: 打印日志级别名称
  • %(filename)s: 打印当前执行程序名
  • %(lineno)d: 打印日志的当前行号
  • %(asctime)s: 打印日志的时间
  • %(message)s: 打印日志信息
import logginglogging.basicConfig(level=logging.DEBUG,format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
logging.debug('调试中...')
logging.info('正常运行中...')
logging.warning('警告!')
logging.error('出现错误,继续运行...')
logging.critical('出现错误,程序奔溃!')
"""
2022-06-29 16:09:47,604 - web_logging.py[line:5] - DEBUG: 调试中...
2022-06-29 16:09:47,604 - web_logging.py[line:6] - INFO: 正常运行中...
2022-06-29 16:09:47,604 - web_logging.py[line:7] - WARNING: 警告!
2022-06-29 16:09:47,604 - web_logging.py[line:8] - ERROR: 出现错误,继续运行...
2022-06-29 16:09:47,604 - web_logging.py[line:9] - CRITICAL: 出现错误,程序奔溃!
"""
设置日志文件
logging.basicConfig(level=logging.DEBUG,format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',filename="log.txt",filemode="w")
  • filename是日志保存到的文件
  • filemode是保存模式

为服务器添加日志
import socket
import threading
import logging
from framework import FrameWork# logging日志的配置
logging.basicConfig(level=logging.DEBUG,format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',filename="log.txt",filemode="w")class MyWebServer(object):def __init__(self, port):"""初始化:创建套接字;初始化web框架"""self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)self.server_socket.bind(("localhost", port))self.server_socket.listen(128)self.framework = FrameWork()def start(self):"""启动:建立连接,并开启子线程"""while True:new_socket, address = self.server_socket.accept()logging.info(f"接受来自{address}的连接")sub_thread = threading.Thread(target=self.handle, args=(new_socket,), daemon=True)sub_thread.start()def handle(self, handle_socket):"""利用子线程收发http格式数据"""request_data = handle_socket.recv(4096)if len(request_data) == 0:logging.info("浏览器已断开连接")handle_socket.close()returnrequest_content = request_data.decode("utf-8")print(request_content)# 以\r\n分割各项信息request_list = request_content.split("\r\n")# 提取请求的资源路径request_line = request_list[0]request_line_list = request_line.split(" ")request_method, request_path, request_version = request_line_list# 首页if request_path == "/":request_path = "/index.html"# 动态资源html交给web框架处理if request_path.endswith(".html"):logging.info("动态资源请求:" + request_path)environ = dict()environ["request_path"] = request_pathstatus, headers, response_body = self.framework.handle_request(environ)response_line = f"{request_version} {status}\r\n"response_header = ""for header in headers:response_header += "%s: %s\r\n" % headerresponse_data = (response_line + response_header + "\r\n" + response_body).encode("utf-8")handle_socket.send(response_data)# 断开与浏览器的连接handle_socket.close()# 静态资源静态web服务器处理else:logging.info("静态资源请求:" + request_path)response_line = response_header = ""# 根据请求路径准备好响应行和响应体try:with open("." + request_path, "rb") as request_file:response_body = request_file.read()except (FileExistsError, FileNotFoundError):response_line += f"{request_version} 404 Not Found\r\n"with open("./error.html", "rb") as request_file:response_body = request_file.read()else:response_line += f"{request_version} 200 OK\r\n"finally:# 准备好响应头信息response_header += "Server: MyWebServer2.0\r\n"# 向浏览器发送响应报文response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_bodyhandle_socket.send(response_data)# 断开与浏览器的连接handle_socket.close()def main():port = 8888my_web_server = MyWebServer(port)my_web_server.start()if __name__ == "__main__":main()
import time
import pymysql
import json
import loggingroute_list = []def route(request_path):def decorator(func):route_list.append((request_path, func))def inner(func, *args, **kwargs):result = func(*args, **kwargs)return resultreturn innerreturn decoratorclass FrameWork(object):def __init__(self):self.route_list = route_list@staticmethod@route("/index.html")def __index():"""主页处理函数"""# 响应状态status = "200 OK"# 响应头信息response_header = [("Server", "MyWebServer2.0")]# 处理后的数据with open("../template/index.html", "r", encoding="utf-8") as file:file_data = file.read()db_connect = pymysql.connect(host="localhost",port=3306,user="root",password="s1234567890",database="gupiao",charset="utf8")# 获取游标cursor = db_connect.cursor()# 查询sql语句sql = "select * from info;"# 执行sqlcursor.execute(sql)# 获取结果集result = cursor.fetchall()data = ""for row in result:data += '''<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td><input type="button" value="添加" id="toAdd" name="toAdd" systemidvaule="000007"></td></tr>''' % row# 替换模板文件中的模板遍历response_data = file_data.replace("{%content%}", data)return status, response_header, response_data@staticmethod@route("/center.html")def __center():"""个人中心处理函数"""# 响应状态status = "200 OK"# 响应头信息response_header = [("Server", "MyWebServer2.0")]# 处理后的数据with open("../template/center.html", "r", encoding="utf-8") as file:file_data = file.read()time_data = time.ctime()response_data = file_data.replace("{%content%}", time_data)return status, response_header, response_data@staticmethoddef __not_found():"""未找到资源处理函数"""# 响应状态status = "404 Not Found"# 响应头response_header = [("Server", "PWS2.0")]# 处理后的数据response_data = "not found"return status, response_header, response_data@staticmethod@route("/center_data.html")def center_data():"""个人中心数据接口开发"""status = "200 OK"response_header = [("Server", "MyWebServer2.0"), ("Content-Type", "text/html;charset=utf-8")]conn = pymysql.connect(host="localhost",port=3306,user="root",password="s1234567890",database="gupiao",charset="utf8")cursor = conn.cursor()sql = '''select i.code, i.short, i.chg, i.turnover, i.price, i.highs, f.note_info from info as i inner join focus as f on i.id = f.info_id;'''cursor.execute(sql)result = cursor.fetchall()cursor.close()conn.close()# 个人中心数据列表center_data_list = list()# 遍历每一行数据转成字典for row in result:# 创建空的字典center_dict = dict()center_dict["code"] = row[0]center_dict["short"] = row[1]center_dict["chg"] = row[2]center_dict["turnover"] = row[3]center_dict["price"] = str(row[4])center_dict["highs"] = str(row[5])center_dict["note_info"] = row[6]# 添加每个字典信息center_data_list.append(center_dict)# 把列表字典转成json字符串json_str = json.dumps(center_data_list, ensure_ascii=False)return status, response_header, json_strdef handle_request(self, environ):"""framework框架主处理函数"""# 获取动态请求资源路径request_path = environ["request_path"]for path, func in route_list:if request_path == path:result = func()return resultelse:# 没有找到动态资源logging.error("没有设置相应的路由:" + request_path)result = self.__not_found()return result


Python之简易Web框架搭建相关推荐

  1. git web框架搭建_Git,Python Web框架,AI,机器学习,Android,Linux和更多必读内容

    git web框架搭建 上周最受关注的是Kedar Vijay Kulkarni编写的新Git系列中的最新一期,随后是Nicholas Hunt-Walker编写的 Python Web框架系列中的最 ...

  2. python django 动态网页_Django-手撸简易web框架-实现动态网页-wsgiref初识-jinja2初识-python主流web框架对比-00...

    自己动手实现一个简易版本的web框架 在了解python的三大web框架之前,我们先自己动手实现一个. 备注: 这部分重在掌握实现思路,代码不是重点 代码中也有许多细节并未考虑,重在实现思路 手撸一个 ...

  3. 手撸web框架即引入框架思想,wsgierf模块,动静态网页,模板语法jinja2,python三大主流web框架,django安装,三板斧...

    手撸web框架 web框架 什么是web框架? 暂时可理解为服务端. 软件开发架构 C/S架构 B/S架构 # 后端 import socketserver = socket.socket() # 不 ...

  4. python四大主流web框架

    python四大主流web框架 转载自博客:https://www.cnblogs.com/an-wen/p/11330834.html --爱文飞翔 Python 四大主流 Web 编程框架 目前P ...

  5. [转载] Python轻量Web框架Flask使用

    参考链接: Python | 使用Flask进行Web开发简介 Python语言近些年越来越火,其特点是开发迅速,语法简单,可移植等.本人就Python3基础语法写了Demo:https://gith ...

  6. Python之路--WEB框架本质

    一.本质 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. #!/usr/bin/env python #coding:utf-8im ...

  7. python协程框架_FastPy3.0 发布,高性能 python 协程web框架

    FastPy3.0 发布了,FastPy是python领域一个高性能的web框架,底层封装gevent协程模型,使得python原生库操作mysql或者http时自动变成异步模式,使用上又具有djan ...

  8. asp.net web开发框架_用Python开发一个Web框架

    一.Web框架 首先我们今天要做的事是开发一个Web框架.可能听到这你就会想.是不是很难啊?这东西自己能写出来? 如果你有这种疑惑的话,那就继续看下去吧.相信看完今天的内容你也能写出一个自己的Web框 ...

  9. Python超级明星WEB框架Flask

    Flask简介 Flask是一个相对于Django而言轻量级的Web框架. 和Django大包大揽不同,Flask建立于一系列的开源软件包之上,这其中 最主要的是WSGI应用开发库Werkzeug和模 ...

最新文章

  1. UVALive 7138 The Matrix Revolutions(Matrix-Tree + 高斯消元)(2014 Asia Shanghai Regional Contest)...
  2. Java源码解析:ArrayList 和 Iterator 使用上的不同
  3. 16道嵌入式C语言面试题(经典) 预处理器(Preprocessor)
  4. C# WebApi POST 提交
  5. 【iOS开发】崩溃问题汇总
  6. 大数据之-Hadoop3.x_Yarn_资源调度器介绍说明---大数据之hadoop3.x工作笔记0140
  7. bigint在java中用什么表示_即使我确信圣诞老人不存在,我却仍然每年给我的孩子们准备圣诞礼物,为什么?...
  8. 华为AI战略完整披露!2款AI芯片首次曝光,拳打TPU,争锋英伟达
  9. spring注释_Spring注释
  10. 互联网大牛们的电脑桌面,佩服!
  11. Spring-MVC配置Gson做为Message Converter解析Json
  12. Java性能优化的五种方式,让你的Java程序更快、更稳定!
  13. 微信H5开发-采坑记
  14. Hive MetaStore java.lang.NoClassDefFoundError: com/facebook/fb303/FacebookService$Iface
  15. 马上过年了,还在为没抢到回家的车票天天犯愁吗?这些好用的抢票神器赶紧用起来吧!...
  16. macOs 静默安装dmg文件
  17. UVALive 6959 - Judging Troubles
  18. 选择商品的时候,弹不出来商品选择框
  19. 消息传递框架MPNN: Neural Message Passing for Quantum Chemistry
  20. 云教学服务器系统软件多少钱,LanStarMaxi多机房云教学管理平台

热门文章

  1. VA插件突然不能使用,弹出“the security key for....”
  2. 冰封王座1.20(转载)
  3. 规模划分电子计算机,电子计算机按规模划分,可以分为_____。
  4. 新媒体培训的目的和意义体现
  5. 串口通信——串口接收数据,发送数据
  6. Jenkins的安装、部署、启动(完整教程)
  7. python 从开发环境Jenkin下载android安装包
  8. webpack配置一些loder,如boostrap4,css等
  9. 《我的心曾悲伤七次》----卡里·纪伯伦
  10. springboot+mysql+软件工程课程思政微平台的设计与实现 毕业设计-附源码271113