Head First Python 讀書筆記(四)
阿新 • • 發佈:2019-01-01
第六章:儲存和管理資料
開啟,處理,關閉檔案
- 開啟一個檔案
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 <html>')
@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請求屬性
一般來說,有三個屬性是日誌記錄比較重要的:
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_log
的html
程式碼:
{% 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 %}
格式化輸出效果: