首頁 > 科技

神器Jinja2,用 Python 快速生成分析報告

2021-06-25 16:14:30

來源:早起Python

作者:劉早起

01、前言

大家好,我是早起。

在之前的文章中,我們使用 Python 開發了一個簡單的基金購買策略的回測系統。在程式碼執行完畢後,會生成一系列的結果,包含大量圖片、表格如下

此時如果一個一個檢視的話便十分低效,如果能使用一個檔案把全部輸出結果都儲存將會大大提高體驗。

首先想到的當然是 PDF 格式,利用 Python 操作 PDF 也是之前文章分享過很多,想必利用表格+圖片生成一個新的PDF並不困難。研究了一番後,發現確實不難,但是太繁瑣了,並且 PDF 涉及格式、分頁等,如果沒有調整好可能會將一張圖片放在兩頁或者一頁只有一張圖其餘全是空白,十分影響美觀,由於我每次產生的結果並不固定,因此很難找到一個通用的模版,遂放棄。

PDF 既然不行,Word就更不用考慮了,所以只能選擇 html 格式,雖然跨平臺性沒有 PDF 好,但是勝在排版簡單,不需要考慮分頁處理。基於 Python 生成 html 有很多成熟的 web 開發框架可以選擇,但為了整體過程不太複雜,最終選擇 Jinja2 來實現這個需求。

02、jinja2

Jinja2 是一個 Python 的功能齊全的模板引擎,簡單來說就是我們將 html 的主要部分寫好,將需要填充的內容空出來,這樣就是一個模版,之後就可以使用 Jinja2 來自動將模版檔案填充,形成一個完整的 html 檔案。

填入文字

首先我們需要製作一個最簡單的模版檔案template.html,內容如下

<html><head><metahttp-equiv="Content-Type"content="text/html;charset=utf-8"><title>report</title></head><h1>基金策略回測報告</h1><body><h2>一、策略詳情</h2><p>策略描述:{{ strategy_name }}</p><p>回測時間段:{{ start_time }} --> {{ end_time }} </p><p>初始本金:{{ money }}</p></body></html>

直接開啟如下

其中被 {{}} 包起來的變數,就是我們需要用 Python 傳遞給模版的,很明顯上方程式碼缺少四個變數,下面是 Python 程式碼

import pandas as pdfrom jinja2 import Environment, FileSystemLoaderdata = {'strategy_name': '第一個策略', 'start_time': '2020-01-01', 'end_time': '2021-06-01', 'money': 20000}env = Environment(loader=FileSystemLoader('./'))template = env.get_template('template.html')with open("out.html", 'w+', encoding='utf-8') as f: out = template.render(strategy_name=data['strategy_name'], start_time=data['start_time'], end_time=data['end_time'], money=data['money']) f.write(out) f.close()

在上面的程式碼中,我們使用env.get_template('template.html')讀取模版檔案,並將需要需要傳入的資料寫入字典中,並將 value 通過template.render傳給模版並渲染輸出,現在開啟生成的 out.html 內容如下

可以看到目標位置的文字都被正確填充,當然填入文字是最基本的操作,下面繼續介紹如何自動創建表格。

填入表格

其實填入表格和填入文字本質上是一樣的,只不過需要使用迴圈來操作,例如將回測指標彙總.xlsx新增到html中

首先在模版檔案中創建一個表格

<h2>二、回測結果</h2><tableborder="1"width = "40%"cellspacing='0'cellpadding='0'><tr><th>基金名稱</th><th>總投入本金</th><th>總收益率</th><th>最大回測率</th></tr> {% for item in items %} <tralign='center'><td>{{ item.基金名稱 }}</td><td>{{ item.消耗本金 }}</td><td>{{ item.總收益率 }}</td><td>{{ item.最大回撤率 }}</td></tr> {% endfor%} </table>

在上面的程式碼中,我們使用創建了一個table,並將表頭寫好,將 cell 內容設定為待填充,注意在jinja2中的迴圈是通過{% %}來完成的。

例如{% for item in items %} 就需要我們傳入一個字典形式 items ,並且裡面包含基金名稱、消耗本金、總收益率、最大回撤率四個key,此時 Python 程式碼修改如下

import pandas as pdfrom jinja2 import Environment, FileSystemLoaderdf = pd.read_excel('回測指標彙總.xlsx')df['消耗本金'] = df['消耗本金'].astype(str) + ' 元'df['最大回撤率'] = df['最大回撤率'].astype(str) + '%'df['總收益率'] = df['總收益率'].astype(str) + '%'data = df.to_dict('records')results = {}results.update({'strategy_name': '第一個策略', 'start_time': '2020-01-01', 'end_time': '2021-06-01', 'money': 20000, 'items': data})env = Environment(loader=FileSystemLoader('./'))template = env.get_template('template.html')with open("out.html", 'w+', encoding='utf-8') as f: out = template.render(strategy_name=results['strategy_name'], start_time=results['start_time'], end_time=results['end_time'], money=results['money'], items = results['items']) f.write(out) f.close()

需要注意的是因為我們需要迴圈操作,所以傳入的 items 就需要是可迭代的,但又需要根據關鍵字匹配值,所以可以將每一行資料轉換為字典,並放在 list 中,這一步可以使用 pandas 讀取excel並直接使用df.to_dict('records')完成(吹一波pandas)

現在執行上面的程式碼,並開啟輸出後的 html

可以看到,Excel的全部內容都被正確的填入,下面介紹如何填入圖片。

填入圖片

其實 html 展示圖片就是通過<a>標籤超連結指向圖片地址,所以本質上和填充文字差不多,只需要在模版檔案中把本地圖片地址預留出來,之後使用 jinja2 傳入即可,例如可以在模版中新增以下內容

<aname="{{ 回測指標 }}"><imgsrc="{{ indicator }}"width="850"></a>此時在 Python 程式碼中指定indicator圖片路徑並傳入即可indicator = 'images/' + '回測指標.png'withopen("out.html", 'w+', encoding='utf-8') as f: out = template.render(strategy_name=results['strategy_name'], start_time=results['start_time'], end_time=results['end_time'], money=results['money'], items = results['items'], indicator = indicator) f.write(out) f.close()

執行後即可正確展示圖片如下

當然我們的圖片不止一張,可以像上面插入表格一樣將圖片地址迴圈填入即可,本文不再贅述,感興趣讀者可以自己研究。

修改樣式

當然,預設生成的 html 樣式可能不夠好看,我們也可以進一步新增一點CSS程式碼來調整,為了簡化可以直接將程式碼寫在標籤中

<styletype="text/css">h1 {margin-left: 20px} h2 {margin-left: 20px; font-size: 19px; font-weight: bold; display: inline-block; padding-left: 10px; border-left: 5px solid #916dd5;} h3 {margin-left: 20px} h4 {margin-left: 20px; margin-bottom: -5px} table {margin-left: 20px; margin-top: 5px; margin-bottom: 5px} p {margin-left: 20px} a {margin-top: -5px;} </style>

主要是添加了一點縮排和間距,讓結果看起來更舒服一點,現在自動生成的 html 檔案部分如下

最後只需要將上述 Python 程式碼封裝,然後在主函數運行結束後自動將相關參數進行傳遞,即可全程自動化生成 html 報告!

當然本文介紹的 Jinja2 操作只是其簡單的一個應用,更多的用法與功能感興趣的讀者可以自己查閱官方文件。


IT145.com E-mail:sddin#qq.com