zoukankan      html  css  js  c++  java
  • Python-WSGI协议,mini-web框架

    本次带给大家的是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)
  • 相关阅读:
    BZOJ2843:极地旅行社(LCT入门题)
    BZOJ2049:Cave 洞穴勘测 (LCT入门)
    【LSGDOJ1383】修改回文 dp
    【NOIP2016】愤怒的小鸟
    【USACO11NOV】牛的阵容Cow Lineup 尺取法+哈希
    【LSGDOJ1836】: 量化交易 贪心
    【网络流24题】魔术球问题 二分答案+最小路径覆盖
    【网络流24题】1745: 餐巾计划问题
    【网络流24题】分配问题 最小最大费用最大流
    【LSGDOJ1834 Tree】树链剖分
  • 原文地址:https://www.cnblogs.com/liudemeng/p/9138257.html
Copyright © 2011-2022 走看看