1. 程式人生 > >Head First Python 讀書筆記(四)

Head First Python 讀書筆記(四)

第六章:儲存和管理資料

開啟,處理,關閉檔案
  • 開啟一個檔案
    todos = open('todos.txt','a')a表示以追加的模式開啟一個檔案,返回一個檔案流(物件)賦給變數todos
  • 寫入資料
    print('this is cat',file=todos),後面這個引數file是用來指定要寫的檔案流;寫完資料之後,記得關閉檔案,防止丟失資料:todos.close()
  • 讀取資料
    task=open('todos.txt'),不帶第二個模式引數,預設是讀取檔案,變數task會接收返回的檔案流,可以使用for ch in task去迴圈輸出裡面的資料,最後最好使用task.close()
    去關閉檔案
模式引數
  • r
    預設模式,讀取資料,並且讀取的檔案要存在
  • a
    追加資料,保留檔案的內容,向檔案末尾增加新資料
  • w
    寫資料,寫之前會清空原檔案的內容
  • x
    開啟一個新檔案來寫資料;如果檔案存在則失敗
with語句
tasks = open('todos.txt')
for ch in tasks:
    print(ch, end='')
tasks.close()

# 使用with語句
with open('todos.txt') as tasks:
    for ch in tasks:
        print(ch, end='')

使用了with

語句,可以不用考慮呼叫close()方法,充當著管理上下文的作用。符合上下文管理協議的約定。

日誌記錄

結合筆記三做的WEB應用,現在需要記錄其web請求的詳細資訊和結果。

  • 增加一個函式,把日誌記錄寫入檔案裡面,在do_search函式裡面去呼叫;傳入當前的請求物件,和執行結果:log_request(request, results)
# req:是當前Flask請求物件  res:查詢結果
def log_request(req: 'flask_request', res: str)->None:
    with open('vsearch.log', 'a') as
log: print(req, res, file=log)

日誌記錄結果
- 當然可以在網頁中去檢視,這就要增加一個URL:/viewlog ,去讀取日誌檔案裡面儲存的資料顯示在瀏覽器中

@app.route('/viewlog')
def view_log()->str:
    with open('vsearch.log') as log:
        contents = log.read()  # 一次性讀取整個檔案
    return contents

網頁訪問結果

轉義資料

根據上面的結果,發現:網頁顯示的和日誌檔案不一樣;那是因為瀏覽器會自動把一些標記隱藏。可以使用escape函式去轉義,對從日誌檔案讀取出來的內容轉義

>>> from flask import escape
>>> escape('this is a request')
Markup('this is a request')
>>> escape('this is a <html>')
Markup('this is a &lt;html&gt;')
@app.route('/viewlog')
def view_log()->str:
    with open('vsearch.log') as log:
        contents = log.read()
    return escape(contents)

就算在網頁上面顯示了web請求物件,但是目前來說,這些請求物件都是一樣的,要檢視請求物件的內部,可以使用dir內建方法,檢視它的方法和屬性列表。

def log_request(req: 'flask_request', res: str)->None:
    with open('vsearch.log', 'a') as log:
        print(str(dir(req)), res, file=log)

網頁顯示的詳細web請求

記錄特定的web請求屬性

一般來說,有三個屬性是日誌記錄比較重要的:

  • req.form:從web應用的html表單提交的資料
    • req.remote_addr:執行web瀏覽器的ip地址
    • req.user_agent:提交資料的瀏覽器標識

修改我們的日誌記錄函式:

def log_request(req: 'flask_request', res: str)->None:
    with open('vsearch.log', 'a') as log:
        print(req.form, file=log)
        print(req.remote_addr,  file=log)
        print(req.user_agent, file=log)
        print(res, file=log)

最後日誌記錄的結果是這樣的:

ImmutableMultiDict([('phrase', 'this is my first python'), ('letters', 'aeiou')]) # 提交的資料
127.0.0.1 # ip地址
Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 # 瀏覽器版本
{'o', 'i'} # 查詢結果

這樣雖然記錄了一次web請求的比較關鍵的資訊,但是卻要從日誌檔案裡面讀取4次,應該想能不能一次web請求,記錄一行資料,然後讀取一次呢?

def log_request(req: 'flask_request', res: str)->None:
    with open('vsearch.log', 'a') as log:
        print(req.form, file=log, end='|')
        print(req.remote_addr,  file=log, end='|')
        print(req.user_agent, file=log, end='|')
        print(res, file=log)
可讀的輸出
  • 字串列表轉為字串join
>>> names=['terry','john','michael']
>>> pythons = '|'.join(names)
>>> pythons
'terry|john|michael'

>>> s =pythons.split('|')       
>>> s       
['terry', 'john', 'michael']
  • 修改view_log函式
# 列表的巢狀  contents  就資料讀入巢狀列表裡面
@app.route('/viewlog')
def view_log()->str:
    contents = []  # 空列表
    with open('vsearch.log') as log:
        for line in log:
            contents.append([])  # 追加一個新的空列表
            for item in line.split('|'):  # 根據 | 分解一行資料
                contents[-1].append(escape(item)) # 把分解的資料放在列表的末尾
    return str(contents)
  • 修改後的輸出
    修改後的輸出
  • Jinja2模板修改輸出log資料,格式化輸出
    {{ }}和{% block %}是模板標記:可以使用html變數和塊作為模板的引數;迴圈變數模板如下,還可以巢狀:
# 希望再 the_row_titles  變數中查詢資料
<tr>
 {% for row_title in the_row_titles %}
        <th>{{ row_title }}</th>
 {{% endfor %}}   # 迴圈結尾
</tr>

使用HTML的表格標記<table><tr><th><td>,格式化輸出日誌資料,接著修改view_log函式,使得其向瀏覽器返回一個html而不是字串

@app.route('/viewlog')
def view_log()->'html':
    contents = []
    with open('vsearch.log') as log:
        for line in log:
            contents.append([])
            for item in line.split('|'):
                contents[-1].append(escape(item))
    titles=('Form Data','Remote_addr','User_agent','Results')
    return render_template('viewlog.html',the_title='View Log',the_row_titles=titles,the_data=contents,)

修改後的view_loghtml程式碼:

{% extends 'base.html' %}{% block body %}

<h2>{{ the_title }}</h2>

<table>
    <tr>
        {% for row_title in the_row_titles %}
        <th>{{ row_title }}</th>
        {% endfor %}
    </tr>
    {% for log_row in the_data %}
    <tr>
        {% for item in log_row %}
        <td>{{item}}</td>
        {% endfor %}
    </tr>
    {% endfor %}
</table>

{% endblock %}

格式化輸出效果:
格式化輸出