Table of Contents
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兩種方式:
- 根據現象縮小範圍
- 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