本次带给大家的是WSGI-mini-web框架, 其中要下载一些网络页面, 大佬们不要见怪.
我所做的mini-web 支持路由, 正则表达式, 添加了log日志功能:解析了url编码可以用
来理解WSGI协议, 一个简单的mini-web框架带给大家.
接下来就是服务器段的代码, 注意大家要在python3下运行, 用到了TCP.
server:
import socket import re import multiprocessing import time # import dynamic.mini_frame import sys class WSGIServer(object): def __init__(self, port, app, static_path): # 1. 创建套接字 self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定 self.tcp_server_socket.bind(("", port)) # 3. 变为监听套接字 self.tcp_server_socket.listen(128) self.application = app self.static_path = static_path def service_client(self, new_socket): """为这个客户端返回数据""" # 1. 接收浏览器发送过来的请求 ,即http请求 # GET / HTTP/1.1 # ..... request = new_socket.recv(1024).decode("utf-8") # print(">>>"*50) # print(request) request_lines = request.splitlines() print("") print(">"*20) print(request_lines) # GET /index.html HTTP/1.1 # get post put del file_name = "" ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0]) if ret: file_name = ret.group(1) # print("*"*50, file_name) if file_name == "/": file_name = "/index.html" # 2. 返回http格式的数据,给浏览器 # 2.1 如果请求的资源不是以.html结尾,那么就认为是静态资源(css/js/png,jpg等) if not file_name.endswith(".html"): try: f = open(self.static_path + file_name, "rb") except: response = "HTTP/1.1 404 NOT FOUND " response += " " response += "------file not found-----" new_socket.send(response.encode("utf-8")) else: html_content = f.read() f.close() # 2.1 准备发送给浏览器的数据---header response = "HTTP/1.1 200 OK " response += " " # 2.2 准备发送给浏览器的数据---boy # response += "hahahhah" # 将response header发送给浏览器 new_socket.send(response.encode("utf-8")) # 将response ic.mini_frame.applicationbody发送给浏览器 new_socket.send(html_content) else: # 2.2 如果是以.py结尾,那么就认为是动态资源的请求 env = dict() # 这个字典中存放的是web服务器要传递给 web框架的数据信息 env['PATH_INFO'] = file_name # {"PATH_INFO": "/index.py"} # body = dynamic.mini_frame.application(env, self.set_response_header) body = self.application(env, self.set_response_header) header = "HTTP/1.1 %s " % self.status for temp in self.headers: header += "%s:%s " % (temp[0], temp[1]) header += " " response = header+body # 发送response给浏览器 new_socket.send(response.encode("utf-8")) # 关闭套接 new_socket.close() def set_response_header(self, status, headers): self.status = status self.headers = [("server", "mini_web v8.8")] self.headers += headers def run_forever(self): """用来完成整体的控制""" while True: # 4. 等待新客户端的链接 new_socket, client_addr = self.tcp_server_socket.accept() # 5. 为这个客户端服务 p = multiprocessing.Process(target=self.service_client, args=(new_socket,)) p.start() new_socket.close() # 关闭监听套接字 self.tcp_server_socket.close() def main(): """控制整体,创建一个web 服务器对象,然后调用这个对象的run_forever方法运行""" if len(sys.argv) == 3: try: port = int(sys.argv[1]) # 7890 frame_app_name = sys.argv[2] # mini_frame:application except Exception as ret: print("端口输入错误。。。。。") return else: print("请按照以下方式运行:") print("python3 xxxx.py 7890 mini_frame:application") return # mini_frame:application ret = re.match(r"([^:]+):(.*)", frame_app_name) if ret: frame_name = ret.group(1) # mini_frame app_name = ret.group(2) # application else: print("请按照以下方式运行:") print("python3 xxxx.py 7890 mini_frame:application") return with open("./web_server.conf") as f: conf_info = eval(f.read()) # 此时 conf_info是一个字典里面的数据为: # { # "static_path":"./static", # "dynamic_path":"./dynamic" # } sys.path.append(conf_info['dynamic_path']) # import frame_name --->找frame_name.py frame = __import__(frame_name) # 返回值标记这 导入的这个模板 app = getattr(frame, app_name) # 此时app就指向了 dynamic/mini_frame模块中的application这个函数 # print(app) wsgi_server = WSGIServer(port, app, conf_info['static_path']) wsgi_server.run_forever() if __name__ == "__main__": main()
min-web代码: 注意了我在自己电脑的文件夹下有静态的网页数据,所以要看运行结果的话就去down下静态网页, 仅供参考.
import re import urllib.parse import logging from pymysql import connect """ URL_FUNC_DICT = { "/index.html": index, "/center.html": center } """ URL_FUNC_DICT = dict() def route(url): def set_func(func): # URL_FUNC_DICT["/index.py"] = index URL_FUNC_DICT[url] = func def call_func(*args, **kwargs): return func(*args, **kwargs) return call_func return set_func @route(r"/index.html") def index(ret): with open("./templates/index.html") as f: content = f.read() # my_stock_info = "哈哈哈哈 这是你的本月名称....." # content = re.sub(r"{%content%}", my_stock_info, content) # 创建Connection连接 conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8') # 获得Cursor对象 cs = conn.cursor() cs.execute("select * from info;") stock_infos = cs.fetchall() cs.close() conn.close() tr_template = """<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="%s"> </td> </tr> """ html = "" for line_info in stock_infos: html += tr_template % (line_info[0],line_info[1],line_info[2],line_info[3],line_info[4],line_info[5],line_info[6],line_info[7], line_info[1]) # content = re.sub(r"{%content%}", str(stock_infos), content) content = re.sub(r"{%content%}", html, content) return content @route(r"/center.html") def center(ret): with open("./templates/center.html") as f: content = f.read() # my_stock_info = "这里是从mysql查询出来的数据。。。" # content = re.sub(r"{%content%}", my_stock_info, content) # 创建Connection连接 conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8') # 获得Cursor对象 cs = conn.cursor() cs.execute("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;") stock_infos = cs.fetchall() cs.close() conn.close() tr_template = """ <tr> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td> <a type="button" class="btn btn-default btn-xs" href="/update/%s.html"> <span class="glyphicon glyphicon-star" aria-hidden="true"></span> 修改 </a> </td> <td> <input type="button" value="删除" id="toDel" name="toDel" systemidvaule="%s"> </td> </tr> """ html = "" for line_info in stock_infos: html += tr_template % (line_info[0],line_info[1],line_info[2],line_info[3],line_info[4],line_info[5],line_info[6], line_info[0], line_info[0]) # content = re.sub(r"{%content%}", str(stock_infos), content) content = re.sub(r"{%content%}", html, content) return content # 给路由添加正则表达式的原因:在实际开发时,url中往往会带有很多参数,例如/add/000007.html中000007就是参数, # 如果没有正则的话,那么就需要编写N次@route来进行添加 url对应的函数 到字典中,此时字典中的键值对有N个,浪费空间 # 而采用了正则的话,那么只要编写1次@route就可以完成多个 url例如/add/00007.html /add/000036.html等对应同一个函数,此时字典中的键值对个数会少很多 @route(r"/add/(d+).html") def add_focus(ret): # 1. 获取股票代码 stock_code = ret.group(1) # 2. 判断试下是否有这个股票代码 conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8') cs = conn.cursor() sql = """select * from info where code=%s;""" cs.execute(sql, (stock_code,)) # 如果要是没有这个股票代码,那么就认为是非法的请求 if not cs.fetchone(): cs.close() conn.close() return "没有这支股票,大哥 ,我们是创业公司,请手下留情..." # 3. 判断以下是否已经关注过 sql = """ select * from info as i inner join focus as f on i.id=f.info_id where i.code=%s;""" cs.execute(sql, (stock_code,)) # 如果查出来了,那么表示已经关注过 if cs.fetchone(): cs.close() conn.close() return "已经关注过了,请勿重复关注..." # 4. 添加关注 sql = """insert into focus (info_id) select id from info where code=%s;""" cs.execute(sql, (stock_code,)) conn.commit() cs.close() conn.close() return "关注成功...." @route(r"/del/(d+).html") def del_focus(ret): # 1. 获取股票代码 stock_code = ret.group(1) # 2. 判断试下是否有这个股票代码 conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8') cs = conn.cursor() sql = """select * from info where code=%s;""" cs.execute(sql, (stock_code,)) # 如果要是没有这个股票代码,那么就认为是非法的请求 if not cs.fetchone(): cs.close() conn.close() return "没有这支股票,大哥 ,我们是创业公司,请手下留情..." # 3. 判断以下是否已经关注过 sql = """ select * from info as i inner join focus as f on i.id=f.info_id where i.code=%s;""" cs.execute(sql, (stock_code,)) # 如果没有关注过,那么表示非法的请求 if not cs.fetchone(): cs.close() conn.close() return "%s 之前未关注,请勿取消关注..." % stock_code # 4. 取消关注 # sql = """insert into focus (info_id) select id from info where code=%s;""" sql = """delete from focus where info_id = (select id from info where code=%s);""" cs.execute(sql, (stock_code,)) conn.commit() cs.close() conn.close() return "取消关注成功...." @route(r"/update/(d+).html") def show_update_page(ret): """显示修改的那个页面""" # 1. 获取股票代码 stock_code = ret.group(1) # 2. 打开模板 with open("./templates/update.html") as f: content = f.read() # 3. 根据股票代码查询相关的备注信息 conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8') cs = conn.cursor() sql = """select f.note_info from focus as f inner join info as i on i.id=f.info_id where i.code=%s;""" cs.execute(sql, (stock_code,)) stock_infos = cs.fetchone() note_info = stock_infos[0] # 获取这个股票对应的备注信息 cs.close() conn.close() content = re.sub(r"{%note_info%}", note_info, content) content = re.sub(r"{%code%}", stock_code, content) return content @route(r"/update/(d+)/(.*).html") def save_update_page(ret): """"保存修改的信息""" stock_code = ret.group(1) comment = ret.group(2) comment = urllib.parse.unquote(comment) conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8') cs = conn.cursor() sql = """update focus set note_info=%s where info_id = (select id from info where code=%s);""" cs.execute(sql, (comment, stock_code)) conn.commit() cs.close() conn.close() return "修改成功..." def application(env, start_response): start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')]) file_name = env['PATH_INFO'] # file_name = "/index.py" """ if file_name == "/index.py": return index() elif file_name == "/center.py": return center() else: return 'Hello World! 我爱你中国....' """ logging.basicConfig(level=logging.INFO, filename='./log.txt', filemode='a', format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s') logging.info("访问的是,%s" % file_name) try: # func = URL_FUNC_DICT[file_name] # return func() # return URL_FUNC_DICT[file_name]() for url, func in URL_FUNC_DICT.items(): # { # r"/index.html":index, # r"/center.html":center, # r"/add/d+.html":add_focus # } ret = re.match(url, file_name) if ret: return func(ret) else: logging.warning("没有对应的函数....") return "请求的url(%s)没有对应的函数...." % file_name except Exception as ret: return "产生了异常:%s" % str(ret)