主頁 > 後端開發 > 15_Web框架-mini frame

15_Web框架-mini frame

2020-09-10 10:59:58 後端開發

1.WSGI協議概述(Python Web Server Gateway Interface)

    1.WSGI允許開發者將選擇web框架和web服務器分開,可以混合匹配web服務器和web框架,選擇一個適合的配對

    2.WSGI協議在不修改服務器和架構代碼情況下確保了可以在多個架構下運行web服務器

    3.Web服務器必須具備WSGI介面,所有的現代Python Web框架都已具備WSGI介面

    4.WSGI介面讓開發者不對代碼作修改就能使服務器和特點的Web框架協同作業

    5.瀏覽器請求WSGI_Server回應流程圖: https://www.processon.com/view/link/5efcac3507912929cb6b33df

2.定義WSGI介面

def application(environ, start_response):
    """遵循WSGI標準的HTTP處理函式

    :param environ: 一個包含所有HTTP請求資訊的dict物件
    :param start_response: 一個發送HTTP回應的函式
    :return: 回傳body資訊
    """
    start_response('200 OK', [('Content-Type', 'text/html')])
    return 'Hello World!'

3.Web服務器-WSGI協議-web框架傳遞的字典

{
    'HTTP_ACCEPT_LANGUAGE': 'zh-cn',
    'wsgi.file_wrapper': <built-infunctionuwsgi_sendfile>,
    'HTTP_UPGRADE_INSECURE_REQUESTS': '1',
    'uwsgi.version': b'2.0.15',
    'REMOTE_ADDR': '172.16.7.1',
    'wsgi.errors': <_io.TextIOWrappername=2mode='w'encoding='UTF-8'>,
    'wsgi.version': (1,0),
    'REMOTE_PORT': '40432',
    'REQUEST_URI': '/',
    'SERVER_PORT': '8000',
    'wsgi.multithread': False,
    'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'HTTP_HOST': '172.16.7.152: 8000',
    'wsgi.run_once': False,
    'wsgi.input': <uwsgi._Inputobjectat0x7f7faecdc9c0>,
    'SERVER_PROTOCOL': 'HTTP/1.1',
    'REQUEST_METHOD': 'GET',
    'HTTP_ACCEPT_ENCODING': 'gzip,deflate',
    'HTTP_CONNECTION': 'keep-alive',
    'uwsgi.node': b'ubuntu',
    'HTTP_DNT': '1',
    'UWSGI_ROUTER': 'http',
    'SCRIPT_NAME': '',
    'wsgi.multiprocess': False,
    'QUERY_STRING': '',
    'PATH_INFO': '/index.html',
    'wsgi.url_scheme': 'http',
    'HTTP_USER_AGENT': 'Mozilla/5.0(Macintosh;IntelMacOSX10_12_5)AppleWebKit/603.2.4(KHTML,likeGecko)Version/10.1.1Safari/603.2.4',
    'SERVER_NAME': 'ubuntu'
}

4.Web動態服務器-多行程

# html檔案夾網盤鏈接: https://pan.baidu.com/s/1wbZ92KLstff3GurLV_C0lg  密碼: hp9o
# 下載完命令列解壓后放到程式的同級路徑下,解壓命令: tar -zxvf 06_html.tar.gz -C ./

    1.專案目錄結構:

~/Desktop/Python/06_Web動態服務器 $ tree
.
├── html
│   ├── index.html
│   ├── ...
├── web
│   ├── __pycache__
│   │   └── mini_frame.cpython-37.pyc
│   └── mini_frame.py
└── web_server.py

    2.web_server.py檔案代碼

import time
import socket
import sys
import re
import multiprocessing


class WSGIServer(object):
    """定義一個WSGI服務器的類"""

    def __init__(self, port, documents_root):

        # 1. 創建套接字
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 2.設定當服務器先close 即服務器端4次揮手之后資源能夠立即釋放,保證下次運行程式時可以立即系結7890埠
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 3.系結服務器地址
        self.server_socket.bind(("", port))
        # 4.變為監聽套接字
        self.server_socket.listen(128)

        # 設定資源檔案的路徑
        self.documents_root = documents_root

    def set_app(self, app):
        """設定web框架可以呼叫的函式(物件)"""
        self.app = app

    def run_forever(self):
        """回圈運行服務器,等待客戶端鏈接并為客戶端服務"""

        # 等待對方鏈接
        while True:
            # 5.等待客戶端鏈接
            new_socket, new_addr = self.server_socket.accept()
            # 6.創建一個新的行程來完成這個客戶端的請求任務
            new_socket.settimeout(3)  # 設定套接字的超時檢測3s
            new_process = multiprocessing.Process(target=self.deal_with_request, args=(new_socket,))
            new_process.start()
            # 因為子行程已經復制了父行程的套接字等資源,所以父行程呼叫close不會將他們對應的這個鏈接關閉的
            new_socket.close()

    def deal_with_request(self, client_socket):
        """用一個新的行程以長鏈接的方式,為這個瀏覽器服務"""

        while True:
            try:
                # 1.接收瀏覽器發送過來的HTTP請求
                request = client_socket.recv(1024).decode("utf-8")
            except Exception as ret:
                print("========>", ret)
                client_socket.close()
                return

            # 判斷瀏覽器是否關閉
            if not request:
                client_socket.close()
                return

            request_lines = request.splitlines()
            for i, line in enumerate(request_lines):
                print(i, line)

            # 提取請求的檔案(index.html)
            # GET /a/b/c/d/e/index.html HTTP/1.1
            ret = re.match(r"([^/]*)([^ ]+)", request_lines[0])
            # 如果沒有指定訪問哪個頁面,則默認訪問index.html
            # GET / HTTP/1.1
            if ret:
                print("正則提取資料:", ret.group(1))
                print("正則提取資料:", ret.group(2))
                file_name = ret.group(2)
                if file_name == "/":
                    file_name = "/index.html"

            # 2.回傳HTTP格式的資料給瀏覽器
            # 2.1如果請求的資源不是以.py結尾,那么就是請求靜態資源
            if not file_name.endswith(".py"):

                # 讀取檔案資料
                try:
                    f = open(self.documents_root + file_name, "rb")
                except:
                    response_body = "file not found, Please enter the correct URL."

                    response_header = "HTTP/1.1 404 not found\r\n"
                    response_header += "Content-Type: text/html; charset=utf-8\r\n"
                    response_header += "Content-Length: %d\r\n" % (len(response_body))
                    response_header += "\r\n"

                    response = response_header + response_body

                    # 沒有對應的回應,回傳404表示沒有這個頁面
                    client_socket.send(response.encode('utf-8'))

                else:
                    content = f.read()
                    f.close()

                    response_body = content

                    response_header = "HTTP/1.1 200 OK\r\n"
                    response_header += "Content-Length: %d\r\n" % (len(response_body))
                    response_header += "\r\n"

                    # 將header回傳給瀏覽器
                    client_socket.send(response_header.encode('utf-8') + response_body)

            # 2.2如果請求的資源是以.py結尾,那么就是請求動態資源
            else:
                # 準備一個字典,里面存放需要傳遞給web框架的資料
                env = {}
                # 存web回傳的資料
                response_body = self.app(env, self.set_response_headers)

                # 合并header和body
                response_header = "HTTP/1.1 {status}\r\n".format(status=self.headers[0])
                response_header += "Content-Type: text/html; charset=utf-8\r\n"
                response_header += "Content-Length: %d\r\n" % len(response_body)
                for temp_head in self.headers[1]:
                    response_header += "{0}:{1}\r\n".format(*temp_head)

                response = response_header + "\r\n"
                response += response_body

                client_socket.send(response.encode('utf-8'))

    def set_response_headers(self, status, headers):
        """此方法會在web框架中被默認呼叫"""
        # 添加服務器的一些資訊
        response_header_default = [
            ("Data", time.ctime()),
            ("Server", "ItCast-python mini web server")
        ]

        # 將web框架中傳遞回的狀態碼/回應頭資訊存盤起來
        # [字串, [xxxxx, xxx2]]
        self.headers = [status, response_header_default + headers]


# 設定靜態資源訪問的路徑
g_static_document_root = "./html"
# 設定動態資源訪問的路徑
g_dynamic_document_root = "./web"


def main():
    """控制web服務器整體"""
    # python3 web_server.py 7890 mini_frame:application
    if len(sys.argv) == 3:
        try:
            # 獲取web服務器的port
            port = sys.argv[1]
            if port.isdigit():
                port = int(port)
            # 獲取web服務器需要動態資源時,訪問的web框架名字
            web_frame_module_app_name = sys.argv[2]
        except Exception as ret:
            print("埠輸入錯誤...")
            return
    else:
        print("運行方式如: python3 web_server.py 7890 mini_frame:application")
        return

    print("http服務器使用的port:%s" % port)

    # 將動態路徑即存放py檔案的路徑,添加到path中,python解釋器才可以找到路徑
    sys.path.append(g_dynamic_document_root)

    ret = re.match(r"([^:]*):(.*)", web_frame_module_app_name)
    if ret:
        # 獲取模塊名
        web_frame_module_name = ret.group(1)
        # 獲取可以呼叫web框架的應用名稱
        app_name = ret.group(2)

    # 匯入web框架的主模塊,回傳值標記這個匯入的模塊
    web_frame_module = __import__(web_frame_module_name)
    # 獲取那個可以直接呼叫的函式(物件)
    app = getattr(web_frame_module, app_name)

    # print(app)  # for test

    # 啟動http服務器
    http_server = WSGIServer(port, g_static_document_root)
    # 將方法封裝成類的屬性
    http_server.set_app(app)
    # 運行http服務器
    http_server.run_forever()


if __name__ == "__main__":
    main()

    3.mini_frame.py檔案代碼

import time


def application(environ, start_response):
        """遵循WSGI標準的HTTP處理函式

        :param environ: 一個包含所有HTTP請求資訊的dict物件
        :param start_response: 一個發送HTTP回應的函式
        :return: 回傳body資訊
        """
    status = "200 OK"
    response_headers = [("Content-Type", "text/html")]
    start_response(status, response_headers)
    return str(environ) + "==Hello world from a simple WSGI application!--->%s\n" % time.ctime()

    4.完整專案網盤鏈接: https://pan.baidu.com/s/1CpO1502f59gEgwCsXaYXlA  密碼: flnq

5.mini-web框架

    1.專案目錄結構:

~/Desktop/python3/07_mini-web框架 $ tree -L 2
.
├── __init__.py
├── dynamic  # 存放py模塊
│   ├── __init__.py
│   ├── __pycache__
│   └── mini_frame.py  # mini框架
├── log.txt  # 日志
├── readme.txt  # 說明檔案
├── run.sh  # shell運行腳本
├── static
│   ├── css
│   └── js
├── stock_db.sql  # 資料庫資料準備生成腳本
├── templates  # 存放模板檔案
│   ├── center.html  # 個人資訊界面
│   ├── index.html  # 股票資訊界面
│   └── update.html  # 修改備注頁面
├── web_server.conf  # mini web服務器組態檔
└── web_server.py  # mini web服務器

    2.資料庫準備

-- 創建資料庫
create database stock_db charset=utf8;
-- 選中資料庫
use stock_db;
-- 匯入資料,sql檔案在完整專案網盤鏈接解壓檔案中
source stock_db.sql

    3.web_server.py檔案代碼

import time
import socket
import sys
import re
import multiprocessing


class WSGIServer(object):
    """定義一個WSGI服務器的類"""

    def __init__(self, port, documents_root):

        # 1. 創建套接字
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 2.設定當服務器先close 即服務器端4次揮手之后資源能夠立即釋放,保證下次運行程式時可以立即系結7890埠
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 3.系結服務器地址
        self.server_socket.bind(("", port))
        # 4.變為監聽套接字
        self.server_socket.listen(128)

        # 設定資源檔案的路徑
        self.documents_root = documents_root

    def set_app(self, app):
        """設定web框架可以呼叫的函式(物件)"""
        self.app = app

    def run_forever(self):
        """回圈運行服務器,等待客戶端鏈接并為客戶端服務"""

        # 等待對方鏈接
        while True:
            # 5.等待客戶端鏈接
            new_socket, new_addr = self.server_socket.accept()
            # 6.創建一個新的行程來完成這個客戶端的請求任務
            new_socket.settimeout(3)  # 設定套接字的超時監測3s
            new_process = multiprocessing.Process(target=self.deal_with_request, args=(new_socket,))
            new_process.start()
            # 因為子行程已經復制了父行程的套接字等資源,所以父行程呼叫close不會將他們對應的這個鏈接關閉的
            new_socket.close()

    def deal_with_request(self, client_socket):
        """用一個新的行程以長鏈接的方式,為這個瀏覽器服務"""

        while True:
            try:
                # 1.接收瀏覽器發送過來的HTTP請求
                request = client_socket.recv(1024).decode("utf-8")
            except Exception as ret:
                print("========>", ret)
                client_socket.close()
                return

            # 判斷瀏覽器是否關閉
            if not request:
                client_socket.close()
                return

            request_lines = request.splitlines()
            for i, line in enumerate(request_lines):
                print(i, line)

            # 提取請求的檔案(index.html)
            # GET /a/b/c/d/e/index.html HTTP/1.1
            ret = re.match(r"([^/]*)([^ ]+)", request_lines[0])
            # 如果沒有指定訪問哪個頁面,則默認訪問index.html
            # GET / HTTP/1.1
            if ret:
                print("正則提取資料:", ret.group(1))
                print("正則提取資料:", ret.group(2))
                file_name = ret.group(2)
                if file_name == "/":
                    file_name = "/index.html"

            # 2.回傳HTTP格式的資料給瀏覽器
            # 2.1如果請求的資源不是以.html結尾,那么就是請求靜態資源
            if not file_name.endswith(".html"):

                # 讀取檔案資料
                try:
                    print(self.documents_root + file_name)
                    f = open(self.documents_root + file_name, "rb")
                except:
                    response_body = "file not found, 請輸入正確的URL..."

                    response_header = "HTTP/1.1 404 not found\r\n"
                    response_header += "Content-Type: text/html; charset=utf-8\r\n"
                    response_header += "Content-Length: %d\r\n" % (len(response_body.encode("utf-8")))
                    response_header += "\r\n"

                    response = response_header + response_body

                    # 沒有對應的回應,回傳404表示沒有這個頁面
                    client_socket.send(response.encode("utf-8"))

                else:
                    content = f.read()
                    f.close()

                    response_body = content

                    response_header = "HTTP/1.1 200 OK\r\n"
                    response_header += "Content-Length: %d\r\n" % (len(response_body))
                    response_header += "\r\n"

                    # 將靜態請求回傳給瀏覽器
                    client_socket.send(response_header.encode('utf-8') + response_body)

            # 2.2如果請求的資源是以.html結尾,那么就是請求動態資源
            else:
                # 準備一個字典,里面存放需要傳遞給web框架的資料
                env = dict()
                env["PATH_INFO"] = file_name  # 例如 index.py
                # 存web回傳的資料
                response_body = self.app(env, self.set_response_headers)

                # 合并header和body
                response_header = "HTTP/1.1 {status}\r\n".format(status=self.headers[0])
                response_header += "Content-Type: text/html; charset=utf-8\r\n"
                response_header += "Content-Length: %d\r\n" % len(response_body.encode("utf-8"))
                for temp_head in self.headers[1]:
                    response_header += "{0}:{1}\r\n".format(*temp_head)

                response = response_header + "\r\n"
                response += response_body

                client_socket.send(response.encode('utf-8'))

    def set_response_headers(self, status, headers):
        """此方法會在web框架中被默認呼叫"""
        # 添加服務器的一些資訊
        response_header_default = [
            ("Data", time.ctime()),
            ("Server", "ItCast-python mini web server")
        ]

        # 將web框架中傳遞回的狀態碼/回應頭資訊存盤起來
        # [字串, [xxxxx, xxx2]]
        self.headers = [status, response_header_default + headers]


# 設定靜態資源訪問的路徑
# g_static_document_root = "./html"
# # 設定動態資源訪問的路徑
# g_dynamic_document_root = "./web"


def main():
    """控制web服務器整體"""
    # python3 web_server.py 7890 mini_frame:application
    if len(sys.argv) == 3:
        try:
            # 獲取web服務器的port
            port = sys.argv[1]
            if port.isdigit():
                port = int(port)
            # 獲取web服務器需要動態資源時,訪問的web框架名字
            web_frame_module_app_name = sys.argv[2]
        except Exception as ret:
            print("埠輸入錯誤...")
            return
    else:
        print("運行方式如: python3 web_server.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"
    # }

    print("http服務器使用的port:%s" % port)

    # 將動態路徑即存放py檔案的路徑,添加到path中,python解釋器才可以找到路徑
    sys.path.append(conf_info["dynamic_path"])

    ret = re.match(r"([^:]*):(.*)", web_frame_module_app_name)
    if ret:
        # 獲取模塊名
        web_frame_module_name = ret.group(1)
        # 獲取可以呼叫web框架的應用名稱
        app_name = ret.group(2)

    # 匯入web框架的主模塊,回傳值標記這個匯入的模塊
    web_frame_module = __import__(web_frame_module_name)
    # 獲取那個可以直接呼叫的函式(物件)
    app = getattr(web_frame_module, app_name)

    # print(app)  # for test

    # 啟動http服務器
    http_server = WSGIServer(port, conf_info["static_path"])
    # 將方法封裝成類的屬性
    http_server.set_app(app)
    # 運行http服務器
    http_server.run_forever()


if __name__ == "__main__":
    main()

    4.mini_frame.py檔案代碼

# import time
# import os
import re
from urllib.parse import unquote
import logging

import pymysql

template_root = "./templates"

# 用來存放url路由映射
# url_route = {
#   "/index.html": index_func,
#   "/center.html": center_func
# }
g_url_route = dict()


def route(url):
    def set_func(func):
        # 添加鍵值對,key是需要訪問的url,value是當這個url需要訪問的時候,需要呼叫的函式參考
        g_url_route[url] = func

        def call_func(*args, **kwargs):
            return func(*args, **kwargs)

        return call_func

    return set_func


@route(r"/index.html")
def index(ret):
    """回傳index.py需要的頁面內容"""
    # return "hahha" + os.getcwd()  # for test 路徑問題
    try:
        f = open(template_root + "/index.html")
    except Exception as ret:
        return "打開股票資訊頁面產生了例外: %s" % ret
    else:
        content = f.read()
        f.close()

        # --------通過資料庫更新股票資訊資料-------
        # 創建connection連接
        db = pymysql.connect(host="localhost", port=3306, user="root", password="123456", database="stock_db",
                             charset="utf8")
        # 獲取cursor物件
        cursor = db.cursor()
        sql = """select * from info;"""
        cursor.execute(sql)
        data_from_mysql = cursor.fetchall()
        cursor.close()
        db.close()

        html_template = """
            <tr>
                <td>%d</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="https://www.cnblogs.com/tangxuecheng/p/添加" id="toAdd" name="toAdd" systemidvaule="%s">
                </td>
                </tr>"""

        html = ""

        for info in data_from_mysql:
            html += html_template % (info[0], info[1], info[2], info[3], info[4], info[5], info[6], info[7], info[1])

        content = re.sub(r"\{%content%\}", html, content)

        return content


@route(r"/center.html")
def center(ret):
    """回傳center.html需要的頁面內容"""
    # return "hahha" + os.getcwd()  # for test 路徑問題
    try:
        f = open(template_root + "/center.html")
    except Exception as ret:
        return "打開個人中心頁面產生了例外: %s" % ret
    else:
        content = f.read()
        f.close()

        # --------通過資料庫更新個人中心資料-------
        # 創建connection連接
        db = pymysql.connect(host="localhost", port=3306, user="root", password="123456", database="stock_db",
                             charset="utf8")
        # 獲取cursor物件
        cursor = db.cursor()
        sql = """select 
        i.code,i.short,i.chg,i.turnover,i.price,i.highs,j.note_info from info as i 
        inner join focus as j on i.id=j.info_id;"""
        cursor.execute(sql)
        data_from_mysql = cursor.fetchall()
        cursor.close()
        db.close()

        html_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"  href="https://www.cnblogs.com/update/%s.html"> <span  aria-hidden="true"></span> 修改 </a>
                </td>
                <td>
                    <input type="button" value="https://www.cnblogs.com/tangxuecheng/p/洗掉" id="toDel" name="toDel" systemidvaule="%s">
                </td>
            </tr>
            """

        html = ""

        for info in data_from_mysql:
            html += html_template % (info[0], info[1], info[2], info[3], info[4], info[5], info[6], info[0], info[0])

        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.判斷試下是否有這個股票代碼
    # 創建connection連接
    db = pymysql.connect(host="localhost", port=3306, user="root", password="123456", database="stock_db",
                         charset="utf8")
    cursor = db.cursor()
    sql = """select * from info where code=%s;"""
    cursor.execute(sql, (stock_code,))
    # 如果要是沒有這個股票代碼,那么就認為是非法的請求
    if not cursor.fetchone():
        cursor.close()
        db.close()
        return "沒有這支股票,大哥,我們是創業公司,請手下留情..."

    # 3.判斷以下是否已經關注過
    sql = """ select * from info as i inner join focus as f on i.id=f.info_id where i.code=%s;"""
    cursor.execute(sql, (stock_code,))
    # 如果查出來了,那么表示已經關注過
    if cursor.fetchone():
        cursor.close()
        cursor.close()
        return "已經關注過了,請勿重復關注..."

    # 4.添加關注
    sql = """insert into focus (info_id) select id from info where code=%s;"""
    cursor.execute(sql, (stock_code,))
    db.commit()
    cursor.close()
    db.close()

    return "關注成功...."


@route(r"/del/(\d+)\.html")
def del_focus(ret):
    """取消對應股票的關注"""
    # 1.獲取股票代碼
    stock_code = ret.group(1)

    # 2.判斷試下是否有這個股票代碼
    db = pymysql.connect(host="localhost", port=3306, user="root", password="123456", database="stock_db",
                         charset="utf8")
    cursor = db.cursor()
    sql = """select * from info where code=%s;"""
    cursor.execute(sql, (stock_code,))
    # 如果要是沒有這個股票代碼,那么就認為是非法的請求
    if not cursor.fetchone():
        cursor.close()
        db.close()
        return "沒有這支股票,大哥,我們是創業公司,請手下留情..."

    # 3. 判斷以下是否已經關注過
    sql = """ select * from info as i inner join focus as f on i.id=f.info_id where i.code=%s;"""
    cursor.execute(sql, (stock_code,))
    # 如果沒有關注過,那么表示非法的請求
    if not cursor.fetchone():
        cursor.close()
        db.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);"""
    cursor.execute(sql, (stock_code,))
    db.commit()
    cursor.close()
    db.close()

    return "取消關注成功...."


@route(r"/update/(\d*)\.html")
def update(ret):
    """顯示更新頁面的內容"""
    # 1.獲取股票代碼
    stock_code = ret.group(1)

    # 2.打開模版
    try:
        template_file_name = template_root + "/update.html"
        f = open(template_file_name)
    except Exception as ret:
        return "%s...沒有找到%s" % (ret, template_file_name)
    else:
        content = f.read()
        f.close()

        # 3. 根據股票代碼查詢相關的備注資訊
        db = pymysql.connect(host="localhost", port=3306, user="root", password="123456", database="stock_db",
                             charset="utf8")
        cursor = db.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;"""
        cursor.execute(sql, (stock_code,))
        stock_note_info = cursor.fetchone()
        cursor.close()
        db.close()

        content = re.sub(r"\{%code%\}", stock_code, content)
        content = re.sub(r"\{%note_info%\}", str(stock_note_info[0]), content)

        return content


@route(r"/update/(\d*)/(.*)\.html")
def update_note_info(ret):
    """進行資料的真正更新"""

    stock_code = ret.group(1)
    stock_note_info = ret.group(2)
    stock_note_info = unquote(stock_note_info)  # 在向資料庫存盤時進行url解碼

    # 創建connection連接
    db = pymysql.connect(host="localhost", port=3306, user="root", password="123456", database="stock_db",
                         charset="utf8")
    # 獲取cursor物件
    cursor = db.cursor()
    sql = """update focus set note_info=%s where info_id = (select id from info where code=%s);"""
    cursor.execute(sql, (stock_note_info, stock_code))
    db.commit()
    cursor.close()
    db.close()

    return "修改成功"


def application(environ, start_response):
    """遵循WSGI標準的HTTP處理函式

    :param environ: 一個包含所有HTTP請求資訊的dict物件
    :param start_response: 一個發送HTTP回應的函式
    :return: 回傳body資訊
    """
    status = '200 OK'
    response_headers = [('Content-Type', 'text/html')]
    start_response(status, response_headers)

    file_name = environ['PATH_INFO']
    # 添加log日志功能
    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:
        # return g_url_route[file_name](file_name)  # 路由功能
        for url, call_func in g_url_route.items():
            print(url)
            ret = re.match(url, file_name)
            if ret:
                return call_func(ret)
        else:
            logging.warning("沒有對應的函式....")
            return "請求的url(%s)沒有對應的函式...." % file_name

    except Exception as ret:
        return "mini_frame框架產生了例外: %s" % ret

    5.完整專案網盤鏈接: https://pan.baidu.com/s/1i7qdYFl0N10_AUrjJPK3Ow 密碼: o1qc

    6.未實作的功能
        1.把操作資料庫的部分單獨封裝到一個函式中,使用時直接呼叫
        2.配置MySQL主從服務器-從服務器查詢操作,主服務器增刪改操作
        3.用元類實作ORM-即操作資料庫部分用元類實作

6.標準庫模塊實作Web動態服務器

try:
    # Python2中的匯入
    from BaseHTTPServer import BaseHTTPRequestHandler
    from BaseHTTPServer import HTTPServer
except ImportError:
    # Python3中的匯入
    from http.server import BaseHTTPRequestHandler
    from http.server import HTTPServer


class RequestHander(BaseHTTPRequestHandler):
    """請求處理類"""

    def do_GET(self):
        """固定的處理GET請求方法"""
        # 查看請求頭
        print(self.headers)
        print("Do method get")
        # 組織回應行
        self.send_response(200)
        # 組織回應頭
        self.send_header("Content-Type", "text/html")
        self.send_header("charset", "utf-8")
        # 回應頭結束
        self.end_headers()
        # 發送回應體
        self.wfile.write(b"<h1>Hello World!</h1>")
        return

    def do_POST(self):
        """固定的處理POST請求方法"""
        pass


def main():
    # 指定地址
    address = ("0.0.0.0", 7890)
    # 生成服務器物件
    server = HTTPServer(address, RequestHander)
    # 運行
    server.serve_forever()


if __name__ == "__main__":
    main()

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/1951.html

標籤:Python

上一篇:用 python 實作破解 zip 壓縮包程式

下一篇:Python基礎語法知識點匯集

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more