python筆記5:編寫Web框架支持路由正則、log功能

Table of Contents

02進程與程序的區別

03關注股票

04取消關注

05更新備註

06 url編碼解碼

07 log日誌


01功能介紹

需求:路由支持正則,添加、刪除關注,修改備註信息(url編碼),logging日誌模塊

02進程與程序的區別

程序(靜態)爲在硬盤中的二進制exe文件,運行時創建一個進程(動態)

同一個程序可以多次運行,對應多個進程(如同用菜譜做菜)

進程可以擁有、使用操作系統的資源

03關注股票

需求:實現添加關注功能:取出參數值,在mysql插入數據

通過正則使帶參url路由對應該函數

@route(r"/add/\d+\.py")

def add_focus():

    return "add ok"

\d+接收參數

‘.’號在正則匹配中需要用\轉義

此時字典的樣子如下:

url_func_dict = {

          r"/add/\d+\.py$": add_focus,

          r"/index\.py": index

        }

在application中處理url

#原

func = url_func_dict[path_info]  # 如果path_info是/index.py那麼也就意味着取 index函數的引用

        # 那麼將來調用的時候,就調用了不一樣的函數

        response_body = func()

#改爲

for r_url, func in url_func_dict.items():

            ret = re.match(r_url, path_info)   # 當此次for循環的時候,r_url是原字符串 (正則表達式),而func指向了這個正則表達式匹配的情況下

                                                # 需要調用的函數

            if ret:

                response_body = func()

此處獲得了正則的匹配結果ret,但應該在add_focus取值,而不應該在外面取,如果在application中取值,接下來所有帶分組的都要取,所以直接傳回fun

if ret:

                response_body = func(ret)

在函數中接收,每個函數的定義時都需要定義形參

@route(r"/add/(\d+)\.py")

def add_focus(ret):

    stock_code=ret.group(1)

    return "add (%s) ok" % stock_code

(\d+)作爲一個group,否則取時會報錯:產生了異常:no such group

添加update.html模板文件

在index中引入jQuery,添加js函數:

<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 + ".py", function(data, status){

                            alert("數據: " + data + "\n狀態: " + status);

                        });

                    }); 

                }); 

        });

    </script>

點擊添加可以獲得成功彈框提示

瀏覽器先要服務器再給框架,處理完了再返回瀏覽器,常常看不到處理過程。只有保證有返回值,才能根據返回信息處理

目前已經獲取了股票代碼,還要判斷是否有這隻股票,如果有,則判斷是否已經關注過

select * from info where code="000007";

select * from focus where info_id="2";

沒關注過則添加關注

insert into focus (info_id) values ("1");

insert into focus (info_id) select id from info where code="";

Add函數:

@route(r"/add/(\d+)\.py")

def add_focus(ret):

    stock_code=ret.group(1)

    #判斷是否有這隻股票

    conn = pymysql.connect(host='localhost',port=3306,user='root',password='123456',database='stock_db',charset='utf8')

    cursor = conn.cursor()

    # sql = """select * from info where code=%s;"""

    sql = """select * from info where code=%s;"""

    cursor.execute(sql,(stock_code))#防止sql注入

    data_from_mysql=cursor.fetchone()

    if not data_from_mysql:

        cursor.close()

        conn.close()

        return "該code不存在"

    #如果有,則判斷是否已經關注過

    sql = """select * from focus where info_id=%s;"""

    cursor.execute(sql,(data_from_mysql[0]))

    #如果查出來了,則已關注過

    if cursor.fetchone():

        # data_from_mysql=cursor.fetchone()

        cursor.close()

        conn.close()

        return "已關注過"

    #添加關注

    sql = """insert into focus (info_id) values ("%s");"""

    cursor.execute(sql,(data_from_mysql[0]))

    #添加需要提交事務

    conn.commit()

    cursor.close()

    conn.close()

   

    return "add (%s) ok" % data_from_mysql[0]

04取消關注

添加可以通過get方式使用url參數,但股票code是明碼,也可以使用post方式

先取出代碼,判斷各種條件合適,操作數據庫

查看前端點擊按鈕時發送的請求,確定裝飾器的url

@route(r"/del/(\d+)\.py")

判斷將沒有關注過改爲非法請求

按照info_id刪除關注

delete from focus where info_id =”1”;

此時注意將個人中心使用的模板中的修改備註、刪除關注的代碼改爲股票代碼,

並在模板html中添加js:

<script>

        $(document).ready(function(){

 

                $("input[name='toDel']").each(function(){ 

                    var currentAdd = $(this); 

                    currentAdd.click(function(){ 

                        code = $(this).attr("systemIdVaule");

                        // alert("/add/" + code + ".html");

                        $.get("/del/" + code + ".py", function(data, status){

                            alert("數據: " + data + "\n狀態: " + status);

                        });

                    }); 

                }); 

        });

    </script>

05更新備註

點擊按鈕,打開新頁面修改,新頁面中會顯示原先的信息

點擊修改按鈕時,可以看到服務器發出請求:update/000007.py

添加新路由:

@route(r"/update/(\d+)\.py")

def update_page(ret):

打開使用的模板修改爲update.html

查看該update.html結構,其中修改欄形如

正在修改:

{%code%}

{%note_info%}

所以應先獲得股票代碼、原備註信息,其中股票代碼由url正則參數解析可獲取

select note_info,info_id from focus where info_id = (select id from info where code = "300268");

用查出來的東西替換html模板中的code等內容

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

    content=re.sub(r"\{%note_info%\}",data_from_mysql[0],content)

顯示該頁面的函數爲:

@route(r"/update/(\d+)\.py")

def update_page(ret):

    '''顯示修改的頁面'''

    stock_code=ret.group(1)

    with open("./templates/update.html", encoding='UTF-8') as f:

        content = f.read()

    conn = pymysql.connect(host='localhost',port=3306,user='root',password='123456',database='stock_db',charset='utf8')

    cursor = conn.cursor()

    #根據股票代碼查詢相關信息

    sql = """select note_info,info_id from focus where info_id = (select id from info where code = %s);

"""

    cursor.execute(sql,(stock_code))

    data_from_mysql = cursor.fetchone() 

    cursor.close()

    conn.close()

   

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

    content=re.sub(r"\{%note_info%\}",data_from_mysql[0],content)

 

    # 3. 返回模板數據

    return content

點擊修改按鈕可以看到瀏覽器請求:

update/ {%code%}/ #note_info.py

所以對多參數update的路由需要重新定義

正則數字用d,字符可用w但無法匹配空格等,用*可匹配

@route(r"/update/(\d+)/(.*+)\.py")

def save_update(ret):

    '''保存修改的頁面'''

    stock_code=ret.group(1)

    note_info=ret.group(2)

修改note信息

update focus set note_info="hhhh" where info_id =(select id from info where code= "000036");

保存更新函數

@route(r"/update/(\d+)/(.*)\.py")

def save_update(ret):

    '''保存修改的頁面'''

    stock_code=ret.group(1)

    note_info=ret.group(2)

    #判斷是否有這隻股票

    conn = pymysql.connect(host='localhost',port=3306,user='root',password='123456',database='stock_db',charset='utf8')

    cursor = conn.cursor()

    # sql = """select * from info where code=%s;"""

    sql = """select * from info where code=%s;"""

    cursor.execute(sql,(stock_code))#防止sql注入

    if not cursor.fetchone():

        cursor.close()

        conn.close()

        return "該code不存在"

    #修改note

    sql = """update focus set note_info=%s where info_id =(select id from info where code= %s);"""

    cursor.execute(sql,(note_info,stock_code))

    #更新需要提交事務

    conn.commit()

    cursor.close()

    conn.close()

   

    return "update (%s) ok" % stock_code

存在問題:控空格亂碼

(。・∀・)ノ゙原來東哥還有建站http://howdoit.cn/

現在看的系列對應的是《從零編寫web框架》http://books.howdoit.cn/012-MiniWeb/

哇,一直愁找不到這個html的課件,每次看課上展示用的都是localhost就很奇怪,覺得做了這麼多應該是可以直接放成網站的,果然有啊

06 url編碼解碼

url中的參數,如中文、/等特殊符號,如果不轉碼,有可能會與url格式衝突,所以會進行編碼(一般自動轉了)防止解析出錯。不是頁面內容的編碼,是url的編碼

frame向mysql保存前要解碼

原本直接獲取保存

note_info=ret.group(2)

urllib.parse模塊能夠實現url的編碼(quote)解碼(unquote)

note_info=urllib.parse.unquote(note_info)

瀏覽器中多個空格會按一個空格來處理

各個地方的功能獨立成一個程序,通過協議實現瞭解耦

07 log日誌

解bug兩種方式:

  1. 根據現象縮小範圍
  2. Log日誌(可以調整級別的print)

日誌分5級:1.debug 2.info 3.warring 4.error 5.critical(默認warning)

日誌輸出方式:控制檯、日誌文件

日誌打印:

import logging

logging.info('log-info')

logging.warning('log-warning')

輸出(默認warning以上):

WARNING:root:log-warning

添加級別設置:

logging.basicConfig(level=logging.DEBUG)

則能夠全部顯示

設置格式:

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

輸出:

2020-5-23 12:14:15 - log1.py[line:5] WARNING:log-warning

將日誌保存到文件:w覆寫追加

logging.basicConfig(level=logging.DEBUG,

       filename='./log.txt',

       filemode='w',

       format='%(asctime)s-%(filename)s[line:%(lineno)d]-%(levelname)s:%(message)s')

黑客在攻擊完之後一定會把記錄痕跡(log)清理掉(擦完屁股再走)

排查bug時避免了復現的麻煩

一般流程:

import logging

logger=logging.getLogger()#如果不創建logger,就是默認root

logger.setLevel(logging.INFO)#總開關

#創建寫入handler

logfile='./log.txt'

fh=logging.FileHandler(logfile,mode='a')

fh.setLevel(logging.DEBUG)#輸出到log文件等級的開關

#創建控制檯handler

ch=logging.StreamHandler()

ch.setLevel(logging.WARNING)

#定義輸出格式

formatter=logging.Formatter("%(asctime)s-%(filename)s[line:%(lineno)d]-%(levelname)s:%(message)s")

fh.setFormatter(formatter)

ch.setFormatter(formatter)

#將logger與handler關聯

logger.addHandler(fh)

logger.addHandler(ch)

 

logging.info('log-info')

logging.warning('log-warning')

使用在服務器中:

def application(env, set_header):

    # 1. 調用set_header指向的函數,將response_header傳遞過去

    status = '200 OK'

    response_headers = [('Content-Type', 'text/html; charset=UTF-8')]

    set_header(status, response_headers)

 

    # 提取url

    path_info = env['PATH_INFO']  # /index.py

    logging.basicConfig(level=logging.DEBUG,

        filename='./log.txt',

        filemode='w',

        format='%(asctime)s-%(filename)s[line:%(lineno)d]-%(levelname)s:%(message)s')

    logging.info('訪問了%s'%path_info)

    try:

 

        for r_url, func in url_func_dict.items():

            ret = re.match(r_url, path_info)   # 當此次for循環的時候,r_url是原字符串 (正則表達式),而func指向了這個正則表達式匹配的情況下

                                                # 需要調用的函數

            if ret:

                response_body = func(ret)

    except Exception as ret:

        logging.error('產生了異常%s'%str(ret))

        return "產生了異常:%s" % str(ret)#保證返回web服務器有body

        # response_body = "-----not found you page-----"

 

    # 2. 通過return 將body返回

    return response_body

訪問人數統計:統計log,找用戶羣測試

主從服務器也可用框架配(訪問不同服務器ip

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章