Python構建web應用(進階版)->對網頁HTML優化邏輯顯示


本篇是承接上一篇web應用(入門級)的內容往下順延的,閱讀后將會了解HTML邏輯顯示優化,如下圖所示,從雜亂無章的日志文件到一個整齊的列表顯示。

—————————————————————————— 我是分割線 —————————————————————————————————

6存儲和管理數據

在下面的內容之前需要了解存儲和管理數據的知識。

關於web應用,應該記錄每個web請求的數據。這樣有利於分析這些問題:

已經響應了多少個請求?最常用的字母列表是什么?請求來自哪個IP地址?哪個瀏覽器用的最多?……

打開、處理(process)和關閉文件

 

1.建立一個txt空文件(hh.txt),指定一個變量(a)打開這個文件,后面參數‘a’的含義是采用追加模式打開這個文件。open會返回一個流,賦值給a變量

2.然后打印消息到文件流。

3.完成工作,最后關閉,文件流進行清理。

4.讀是open的默認模式,所以不需要提供模式參數,打開文件時仍需要指定一個變量(read),然后open會返回一個文件流賦給read變量。

5.for循環每次循環讀取一個數據行,沒有數據行即終止。

6.工作完成,關閉文件流進行清理。

 

訪問模式:(r w主要針對文本文件)

訪問模式

說明

r

以只讀方式打開文件,文件的指針會放在文件的開頭。(默認模式)

w

以可寫方式打開文件,文件存在時:覆蓋,不存在時:創建新文件。

a

以追加方式打開文件,文件存在:指針放在文件結尾,不存在時:創建新文件進行寫入。

rb

以二進制格式打開一個文件用於只讀。

wb

以可讀方式打開二進制文件。

ab

以追加方式打開二進制文件。

r+

打開一個文件用於讀寫,文件指針在開頭。

rb+

 

wb+

以二進制格式打開一個文件用於只讀。

ab+

 

x

打開一個新文件進行寫數據,如果文件存在則失敗。

上表沒有寫全,規律:r--read; w--write; a--add to; b--binary; + --讀寫

 

使用with對文件操作

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應用查看日志

下來讓日志顯示在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函數,調用時提供一個字符串,其中不包含任何特殊字符:

對一些包含特殊字符的字符串使用這個函數,它會將<>轉義為&lt和&gt

在第一行調用加入escape然后在后面的return后使用函數:escape(contents)

新的測試結果:

注意到剛才是紅色的字變成了黑色字,,不過這些數字並不能看出來什么。

更改代碼:

只在最后的print做了修改,把req的內容通過dir()列出然后轉成字符輸出。

下來測試新的日志記錄代碼,先完成下列步驟:

  1. 修改log_request與上圖保持一致。
  2. 保存修改后的代碼,這回重啟我們的web應用。
  3. 找到並刪除當前的vsearch.log文件。
  4. 通過瀏覽器輸入3個新搜索。
  5. 使用/viewlog查看新創建的日志。

仔細看看現在的內容,有意義了嗎?

這似乎有些亂,不過仔細看會發現有一些我們查找到的值。

現在可以看到每個請求都有大量的關聯方法和屬性,記錄所有的屬性是沒有意義的。其中有三個對於日志記錄很重要:

  • req.form:從web應用的HTML表單提交的數據。
  • req.remote_addr:運行web瀏覽器的IP地址。
  • req.user_agent:提交數據的瀏覽器標識。

下面對代碼進行進一步的調整。

記錄特定的web請求屬性

 

分別輸出這些內容,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可以將列表轉換成字符串。

下來在>>>中進行測試:

join前面的‘|’意思是使用|將每個字符串連接起來。

  • 而split是將字符串轉換成列表。

使用給定的|,將字符串分割成一個列表。

 

修改代碼:

使用文件打開的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生成可讀的輸出

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>中。

 

后面的放大來看:

大體上可以看出來一些,現在對於網頁的源代碼好像有一些眉目了。

回顧整個代碼:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM