本篇是承接上一篇web應用(入門級)的內容往下順延的,閱讀後將會了解HTML邏輯顯示優化,以下圖所示,從雜亂無章的日誌文件到一個整齊的列表顯示。html
—————————————————————————— 我是分割線 —————————————————————————————————python
在下面的內容以前須要瞭解存儲和管理數據的知識。web
關於web應用,應該記錄每一個web請求的數據。這樣有利於分析這些問題:編程
已經響應了多少個請求?最經常使用的字母列表是什麼?請求來自哪一個IP地址?哪一個瀏覽器用的最多?……flask
1.創建一個txt空文件(hh.txt),指定一個變量(a)打開這個文件,後面參數‘a’的含義是採用追加模式打開這個文件。open會返回一個流,賦值給a變量瀏覽器
2.而後打印消息到文件流。app
3.完成工做,最後關閉,文件流進行清理。webapp
4.讀是open的默認模式,因此不須要提供模式參數,打開文件時仍須要指定一個變量(read),而後open會返回一個文件流賦給read變量。函數
5.for循環每次循環讀取一個數據行,沒有數據行即終止。post
6.工做完成,關閉文件流進行清理。
訪問模式:(r w主要針對文本文件)
訪問模式 |
說明 |
r |
以只讀方式打開文件,文件的指針會放在文件的開頭。(默認模式) |
w |
以可寫方式打開文件,文件存在時:覆蓋,不存在時:建立新文件。 |
a |
以追加方式打開文件,文件存在:指針放在文件結尾,不存在時:建立新文件進行寫入。 |
rb |
以二進制格式打開一個文件用於只讀。 |
wb |
以可讀方式打開二進制文件。 |
ab |
以追加方式打開二進制文件。 |
r+ |
打開一個文件用於讀寫,文件指針在開頭。 |
rb+ |
|
wb+ |
以二進制格式打開一個文件用於只讀。 |
ab+ |
|
x |
打開一個新文件進行寫數據,若是文件存在則失敗。 |
上表沒有寫全,規律:r--read; w--write; a--add to; b--binary; + --讀寫
with open('hh.txt') as a:
for chore in a:
print(chore, end = ' ')
這樣能夠完成和以前的open相同的操做,而且後面不用close()關閉文件。這些內容是在Python內部有一個上下文管理協議,它完成收尾的工做,在須要時調用close。
以前提到的程序能夠在添加內容了,咱們想對這個web應用的數據進行存儲記錄下來。
回顧以前的程序內容:
在下面添加一個函數
def log_request(req: 'flask_request', res: str) -> None:
with open('vsearch.log', 'a') as log:
print(req, res, file=log)
調用這個函數時,req參數做爲當前的flask請求對象,res參數做爲調用vsearch_for_letters函數的結果.而後函數log_request把req和res的值追加到一個名爲vsearch.log的文件。
使用這個函數時,咱們添加到上面的do_search中進行調用,固然在調用以前須要定義函數,因此調整位置後的程序以下:
保存後在webapp文件夾中運行cmd,輸入python vsearch_for_web.py
而後打開瀏覽器輸入地址http://127.0.0.1:5000進行測試,試過幾回以後會發現生成了一個log文件
下來讓日誌顯示在web瀏覽器裏,因此新建一個URL: /viewlog
在最後面添加代碼:
@app.route('/viewlog')
def view_the_log() -> str:
with open('vsearch.log') as log:
contents = log.read()
return contents
保存測試後鍵入http://127.0.0.1:5000/viewlog看到:
這些好像只是瀏覽器接受和顯示最後的結果,並無最開始搜索的內容,檢查網頁的源代碼,直接瀏覽器中右鍵
這些內容和剛纔的並無太大差異,仍是沒有看到搜索的內容,web拒絕顯示用戶搜索的數據,由於HTML中<Request>是一個不合法的標記,瀏覽器會將它忽略。
Flask包含一個escape函數,調用時提供一個字符串,其中不包含任何特殊字符:
對一些包含特殊字符的字符串使用這個函數,它會將<>轉義爲<和>
在第一行調用加入escape而後在後面的return後使用函數:escape(contents)
新的測試結果:
注意到剛纔是紅色的字變成了黑色字,,不過這些數字並不能看出來什麼。
更改代碼:
只在最後的print作了修改,把req的內容經過dir()列出而後轉成字符輸出。
下來測試新的日誌記錄代碼,先完成下列步驟:
仔細看看如今的內容,有意義了嗎?
這彷佛有些亂,不過仔細看會發現有一些咱們查找到的值。
如今能夠看到每一個請求都有大量的關聯方法和屬性,記錄全部的屬性是沒有意義的。其中有三個對於日誌記錄很重要:
下面對代碼進行進一步的調整。
分別輸出這些內容,end=‘|’表示把默認的換行符替換爲|,這樣一個請求的數據就是一行內容。
這是新的日誌文件的內容:能夠看到數據都在一行並且整齊了不少
上文中的連續四個print好像有些多餘,其實能夠縮減到一個print語句中,有一個可選的參數sep,它能夠設置分隔符,默認爲空格。
下來改進那幾行print代碼:
def log_request(req: 'falsk請求', res: str) -> None:
with open('vsearch.log', 'a') as log:
print(req.form, req.remote_addr, req.user_agent, res, file=log, sep='|')
繼續剛纔那五個步驟,就是從新測試的步驟。提示:存代碼、刪日誌、新鍵入。
而後查看URL:/viewlog會發現少了不少
能夠找到裏面有搜索的字符串也有結果,看來已經有意義了。但是如何更加規範這些內容呢?
下面是vsearch.log文件中的一個數據行:
ImmutableMultiDict([('phrase', 'this is s test of the posting capability'), ('letters', 'aeiou')])|127.0.0.1|Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36|{'o', 'a', 'e', 'i'}
三個|分割了四部份內容,第一部分是表單數據。第二部分是遠程機器的IP地址。第三部分是web瀏覽器的標識字符串。最後一部分是函數調用的結果。
下來在>>>中進行測試:
join前面的‘|’意思是使用|將每一個字符串鏈接起來。
使用給定的|,將字符串分割成一個列表。
修改代碼:
使用文件打開的open命令,用|將日誌中的記錄轉換成一個列表,這樣會看起來更加美觀,可讀性強。
須要修改的是view_the_log函數。
def view_the_log() -> str:
contents = []
with open('vsearch.log') as log:
for lines in log:
contents.append([])
for item in lines.split('|'):
contents[-1].append(escape(item))
return escape(contents)
有一些地方須要解釋:
也許你會對contents[-1].append(escape(item))有疑問,理解這行代碼技巧從內向外、從左向右讀。首先是外圍for循環的item開始,它會傳遞到escape,在用append將獲得的字符串追加到contents末尾 ( [-1] ) 。contents是一個嵌套列表。
保存代碼而後進行測試:
如今的輸出變成了一個嵌套列表,而再也不是一個字符串列表。如今咱們使用一個設計的jinja2模板處理contents,就基本能獲得所需的可讀的輸出了。
HTML提供了一組標記來定義表格的內容:包括<table>:一個表格,<th>:一行表格數據,<tr>:一個表格列標題和<td>:一個表格數據項(單元格)。
每一個標記都有對應的一個結束標記</table>,</tr>,</th>和<td>。
若是發現須要生成HTML,就應該使用jinja2模板引擎,它主要是設計用來生成HTML,這個引擎包含一些基本的編程構造,能夠用來「自動實現」須要的顯示邏輯。
下面是一個新模板下載的地址仍是以前的http://python.itcarlow.ie/ed2/,名爲viewlog.html,它能夠將日誌文件中的原始數據轉換成一個HTML表格,這個模板但願傳入contents嵌套列表做爲他的參數。jinja2的for循環構造與Python相似,但須要注意的是行尾不須要冒號,由於%}至關於一個分隔符;每一個循環的代碼組用{% endfor %}結束。
能夠看到,第一個for循環但願在一個名爲the_row_titles的變量中查找數據,而第二個for循環但願獲得the_data中的數據。第三個for循環但願數據是一個數據項列表。
整個表在一個<table>標記中,描述性標題<th>中有單獨的行<tr>標記。每一個日誌數據項放在一個<td>標記中,日誌文件中的各行有單獨的<tr>標記。(如今可能有些困難,不事後面就會理解這幾句話的含義)
這個模板須要放在templates文件夾下面。
要讓viewlog.html調用render_template(render_template的函數,若是指定一個模板名和所需的參數,調用這個函數時會返回一個HTML串。),爲它須要的三個參數分別傳入值。下面建立一個描述性的標題元組,並把它賦值給the_row_titles,而後將contents的值賦給the_data。在呈現這個模板以前,還須要給the_title提供一個適當的值,修改函數view_the_log:
def view_the_log() -> 'html':
contents = []
with open('vsearch.log') as log:
for lines in log:
contents.append([])
for item in lines.split('|'):
contents[-1].append(escape(item))
titles = ('From Data','Remote_addr','User_agent','Results')
return render_template('viewlog.html',
the_title = 'View Log',
the_row_titles = titles,
the_data = contents,)
保存後是flask重啓web應用,而後進入http://127.0.0.1:5000/viewlog查看日誌:
我對這個結果很滿意,由於終於獲得了想要的輸出,而且看起來很整齊。
點擊右鍵查看網頁源代碼,會看到日誌的每個數據項都放在它本身的<td>標記中,每一個數據行也有本身的<tr>標記,整個表放在一個HTML的<table>中。
後面的放大來看:
大致上能夠看出來一些,如今對於網頁的源代碼好像有一些眉目了。
回顧整個代碼: