Python运维(五)--Jinja2、word模板及Excel

一、Jinja2

1.1 概述

  • 模板系统:模板文件中放置特定的占位符、占位语句,python程序文件用变量值来替换模板文件中的占位符
  • Jinja2:由flask作者开发的一款模板语言,原始目的为了web网页开发
  • 安装:pip3 install Jinja2

1.2 语法

  • 语法种类:注释:{# #},变量取值:{{ }},控制结构:{% %}
  • 在Jinja2渲染:传送门,以下语句可在其测试
  • 一个不错的Jinja2博客:传送门

1.2.1 变量

  • 代码示例
    {# 这里是注释的写法,文件名index.html #}
    <p> 来自字典的值:{{ mydict['one'] }}</p>
    <p> 来自列表的值:{{ mylist[0] }}</p>
    <p> 来自对象方法的值:{{ myobj.meth() }}</p>
    

    注意:大括号内两侧的空格是必须的

  • 过滤器表
    过滤器名说明
    safe渲染时不转意
    upper、lower、capital把值全转大写、小写、首字母大写
    title每个单词首字母大写
    trim去首位空格
    join拼接多个值为字符串
    replace替换字符串值
    round、int对数字四舍五入、值转成整型
  • 用法
    {{ 11.1 | round | int }}
    >>> 11
    {{ "hello world" | replace("hello", "goodbye") | upper }}
    >>> GOODBYE WORLD
    # names为列表:拼接字符为-
    {{ names | join("-") }}
    # 选列表第一个元素去除两侧空
    {{ ['  a  b'] | first | trim }}
    >>> a  b
    # 在给定宽度内将字符串行居中对齐
    {{ '居中标题' | center }}
    # hosts为列表,添加换行符
    {{ hosts | join('\n') }}
    # 添加默认值
    {{ name | default('duke') }}
    # 默认按键对字典排序:按值排序写法dictsort(by='value')
    {% for key, value in dic | dictsort %}
    # 先管道转换,再比较
    {% if str_value | float >= 4.22 %}
    

    注:过滤器通过管道符与变量连接

1.2.2 控制循环结构

  • if语句
    {% if duke.sick %}
        duke is sick
    {% elif duke.dead %}
    	duke is dead
    {% else %}
    	duke is ok
    {% endif %}
    

    比较运算符:==, !=, >, >=, <, <=
    逻辑运算符:and, or, not

  • for语句
    {# 遍历列表:语句排版,按没语句块考虑效果 #}
    <ul>
    {% for user in users %}
    	<li>{{ user }}</li> 
    {% endfor %}
    </ul>
    
    {# 遍历字典 #}
    <ul>
    {% for key, value in users.items() %}
        {% if loop.first %}
    	<li>这是首轮循环</li>         
        {% endif %}    
    	<li>当前键为:{{ key }}</li> 
        <li>当前值为:{{ value }}</li> 
    {% endfor %}
    </ul>
    

    loop.index:从1开始记录循环次数,loop.index()为从0开始
    loop.first:若为第一次迭代则为True,配合if使用,loop.last
    loop.length:循环次数

  • for + if语句:{% for num in nums if num>10 %}

1.2.3 宏

  • 定义位置:建议集中写在模板的尾部
  • 宏定义
    {% macro input(name, age=18) -%}
    <h1>{{ name }}{{ age }}</h1>
    {%- endmacro -%}
    
  • 宏使用
    <h1>{{input('duke', 12)}}</h1>
    

1.3 空行

1.3.1 分析

  • 示例
    在这里插入图片描述
  • 显示:右侧渲染结果有多处空行
  • 原因分析:渲染模板时,所有语言块都将被删除,但所有空都保留在原处。也就是说,如果在块之前或之后有空格、制表符或换行符,都保留

1.3.2 解决方案

  • 方法一:
    • -语句:{%- 语句 %},去除语言块之前的空,{% 语句 -%},去除之后的空(含换行符)
    • 渲染if语句:删除{% if true %}语句块,末尾换行符保留;删除{% if false %}语句块,末尾换行符也删除;删除{% endif %}语句块,不管真假,都保留前后空及换行符
  • 方法二:
    • 启用渲染选项:删除空行trim_blocks,删除块左侧空lstrip_blocks
    • 补充选项:Strict check,若有值未传则报错

1.4 渲染

1.4.1 渲染函数

  • /root/main/main.py
    #!/usr/bin/python
    # -*- codinig: UTF-8 -*-
    
    import os
    import sys
    import jinja2
    
    
    # 定义模板渲染函数:模板路径,填充数据以json格式
    def render(tpl_path='index.html', **kwargs):
        # 拆分路径:路径与文件名
        path, filename = os.path.split(tpl_path)
        # 渲染后的文件内容:模板父文件夹路径,模板文件名,传入字典
        return jinja2.Environment(
            loader=jinja2.FileSystemLoader(path or './'),
            trim_blocks=True,
            lstrip_blocks=True
        ).get_template(filename).render(**kwargs)
    
    # 命令行操作函数
    def cmd_format(**data):
        # 获取命令行参数列表
        filepath = sys.argv
        # 若未输入参数就通过if语句用默认模板
        filepath.append('')
        if filepath[1]:
            out = render(filepath[1], **data)
            print(out)
        else:
            out = render(**data)
            print(out)
    
    
    if __name__ == '__main__':
        # 数据填充字典:json格式
        datainput = {
            'head': "这是标题",
            'context': "这是内容"
        }
        # 这里是命令行方式,也可以直接调用render函数
        cmd_format(**datainput)
    
  • 模板/root/main/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>{{ head }}</title>
    </head>
    <body>
        <div>
            <h1>{{ context }}</h1>
        </div>
    </body>
    </html>
    
  • 运行python3 main.py
  • 终端输出
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>这是标题</title>
    </head>
    <body>
        <div>
            <h1>这是内容</h1>
        </div>
    </body>
    </html>
    

1.4.2 继承和super函数

  • 模板继承:父模板中留block语句块,子模板继承父模板,并只需要写block语句块即可
  • super函数:父模板中block语句块中的语句,在子模板中用{{ super() }}语句可拿到,否则子覆盖父
  • /root/main/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>欢迎</h1>
        <h2>以下是块内容</h2>
        {% block content %}
        <h3>这是块内的语句</h3>
        {%- endblock %}
        <h2>再见</h2>
    </body>
    </html>
    
  • /root/main/login.html
    {% extends "index.html" %}
    
    {% block content %}
    {{ super() }}
        <form>
            用户名:<input type="text" name="user">
            密码:<input type="text" name="pwd">
        </form>
    {% endblock %}
    
  • 运行python3 main.py login.html
  • 终端输出
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>欢迎</h1>
        <h2>以下是内容</h2>	
        <h3>这是块内的语句</h3>	
        <form>
            用户名:<input type="text" name="user">
            密码:<input type="text" name="pwd">
        </form>	
        <h2>再见</h2>
    </body>
    </html>
    

二、word模板

  • docxtpl模块:使用Jinja2思路,以docx为word模板
  • docxtpl 主要依赖两个包:python-docx 用来读写word,jinja2用来管理插入到模板中的标签
  • 官方文档:传送门
  • 语法种类:注释:{#p #},变量取值:{{ }}转义{{ variable|e }}),控制结构:{%p %},p代表段落,否则会出现1.3节的空行现象,注释和控制结构每行只能出现一个,灵活运用1.3.1方法一连接长句子
  • 安装:pip3 install docxtpl

2.1 渲染函数

  • /root/main/main.py

    #!/usr/bin/python
    # -*- codinig: UTF-8 -*-
    
    from docxtpl import DocxTemplate
    import sys
    import jinja2
    
    
    # 定义模板渲染函数:模板路径,输出路径,填充数据以字典格式
    def render(tpl_path='index.docx', output='sample.docx', **kwargs):
        tpl = DocxTemplate(tpl_path)
        # 添加参数:清除空行
        jinja_env = jinja2.Environment(trim_blocks=True, lstrip_blocks=True)
        # 渲染函数:传入数据、jinja2环境变量
        tpl.render(kwargs, jinja_env)
        tpl.save(output)
        print("completed")
    
    # 命令行构造函数
    def cmd_format(**data):
        filepath = sys.argv
        # 保证有三个参数:防止少于两个参数的话报错
        filepath.append('')
        filepath.append('')
        # 指定:模版文件路径、输出文件路径(路径必须存在,否则报错)
        if filepath[1] and filepath[2]:
            render(filepath[1], filepath[2], **data)
        # 指定:模版文件路径、输出文件为./sample.docx
        elif filepath[1]:
            render(filepath[1], **data)
        # 默认:模版文件./index.docx、输出文件为./sample.docx
        else:
            render(**data)
    
    
    if __name__ == '__main__':
        # 数据填充字典:json格式
        datainput = {
            'head': "内容",
            'context': "context内容"
        }
        # 这里是命令行方式,也可以直接调用render函数
        cmd_format(**datainput)
    
  • 模版:/root/main/index.docx
    在这里插入图片描述

  • 运行python3 main.py

  • 输出:/root/main/sample.docx
    在这里插入图片描述

2.2 表格

  • 模版/root/main/index.docx

2.2.1 动态水平合并

  • 模版写法
    {%tc for col in context %}{% hm %}标题{%tc endfor %}
    {%tc for col in context %}{{col}}{%tc endfor %}

    hm:水平动态合并,如下图效果

  • 效果
    在这里插入图片描述

    context:[‘a’, ‘b’, ‘c’]


2.2.2 动态垂直合并

  • 模版写法
    {%tr for row in context %}
    {% vm %}竖标题{{row}}
    {%tr endfor %}

    hm:水平动态合并,如下图效果

  • 效果
    在这里插入图片描述

2.3 插入图片

  • 模版:/root/main/index.docx
    {{ title_pic1 }}
    {{ title_pic2 }}
    {{ title_pic3 }}
    
  • 渲染函数
    #!/usr/bin/python
    # -*- codinig: UTF-8 -*-
    
    import sys
    import jinja2
    from docxtpl import DocxTemplate, InlineImage
    from docx.shared import Mm, Pt
    
    
    # 定义模板渲染函数:模板路径,输出路径,填充数据以字典格式
    def render(tpl_path='index.docx', output='sample.docx', **kwargs):
        tpl = DocxTemplate(tpl_path)
        # 添加参数:清除空行
        jinja_env = jinja2.Environment(trim_blocks=True, lstrip_blocks=True)
        # 展开image项的每个子项,重新添加进kwargs
        if 'images' in kwargs:
            for pic in kwargs['images']:
                width = pic.get('width')
                height = pic.get('height')
                if width and height:
                    # 给数据字典增加项:图片的单位是像素(Pt)、毫米(Mm)
                    kwargs.setdefault(pic.get('name'), InlineImage(tpl, pic.get('path'), width=Pt(width), height=Pt(height)))
                elif width:
                    kwargs.setdefault(pic.get('name'), InlineImage(tpl, pic.get('path'), width=Pt(width)))
                else:
                    kwargs.setdefault(pic.get('name'), InlineImage(tpl, pic.get('path'), height=Pt(height)))
            del kwargs['images']
        # 渲染函数:传入数据、jinja2环境变量
        tpl.render(kwargs, jinja_env)
        tpl.save(output)
        print("completed")
    
    
    # 命令行构造函数
    def cmd_format(**data):
        filepath = sys.argv
        # 保证有三个参数:防止少于两个参数的话报错
        filepath.append('')
        filepath.append('')
        # 指定:模版文件路径、输出文件路径(路径必须存在,否则报错)
        if filepath[1] and filepath[2]:
            render(filepath[1], filepath[2], **data)
        # 指定:模版文件路径、输出文件为./sample.docx
        elif filepath[1]:
            render(filepath[1], **data)
        # 默认:模版文件./index.docx、输出文件为./sample.docx
        else:
            render(**data)
    
    
    if __name__ == '__main__':
        # 数据填充字典:json格式
        datainput = {
            # 若没有内插图像,则删掉此项,不可为空,否则报错
            'images': [
                # 1、指定宽,2、指定高,3、指定宽高
                {'name': 'title_pic1', 'path': 'pic/test.png', 'height': 100},
                {'name': 'title_pic2', 'path': 'pic/test.png', 'width': 100},
                {'name': 'title_pic3', 'path': 'pic/test.png', 'width': 100, 'height': 100}
            ]
        }
        # 这里是命令行方式,也可以直接调用render函数
        cmd_format(**datainput)
    
  • 效果
    在这里插入图片描述

三、Excel操作

3.1 概述

  • openpyxl:一个读写Excel的python库,安装pip3 install openpyxl
  • 层级
    • Workbook:对工作薄(Excel文件)对象的抽象
    • Worksheet:对表单对象的抽象
    • Cell:对单元格对象的抽象
  • 操作工作簿
    在这里插入图片描述

3.2 工作簿对象Wrokbook

  • 新建空Excel
    import openpyxl
    # 创建工作簿对象
    wb = openpyxl.Workbook()
    ...
    # 保存工作簿对象
    wb.save("新建表格.xlsx")
    
  • 打开已存在Excel
    import openpyxl
    # 打开已存在的Excel文件并返回工作簿对象
    wb = openpyxl.load_workbook("新建表格.xlsx")
    ...
    wb.save("新建表格.xlsx")
    
  • workbook对象属性
    写法解释
    wb.read_only判断文件是否以只读打开,返回布尔值
    wb.encoding返回文件编码方式,如’utf-8’
    wb.properties返回文档元数据对象,如作者、创建时间、版本…
    改写wb.properties.version = 1.0
  • 操作Worksheet方法
    # 增表单:第二个参数为插入索引值,可选
    In [1]: wb.create_sheet('boss')
    Out[1]: <Worksheet "boss">
    
    ###################################################
    # 删表单:方法一,若不存在,报错KeyError
    In [2]: wb.remove(wb['boss'])
    # 方法二:del wb['boss'],若不存在,报错KeyError
    
    ###################################################
    # 查表单:获取表单名称列表
    In [3]: wb.sheetnames
    Out[3]: ['student', 'teacher']
    # 获取表单对象:以名称方式
    In [4]: wb["student"]
    Out[4]: <Worksheet "student">
    # 获取当前活动表单对象
    In [5]: wb.active
    Out[5]: <Worksheet "student">
    

3.3 表单对象Worksheet

3.3.1 表单对象属性

  • 表单对象属性
    写法解释
    wb.title表格标题字符串,例如'student'
    wb.dimensions返回表格中含有数据的表格大小,例如'A1:D4'
    ws.max_row返回含数据的最大行数4,还有min_row,max_column,min_column

3.3.2 遍历表格

  • 按行遍历cell对象:按列columns同
    In [1]: for row in ws.rows:
       ...:     print(row)
    # row为每行cell对象的元组,rows为生成器
    (<Cell 'student'.A1>, <Cell 'student'.B1>, <Cell 'student'.C1>, <Cell 'student'.D1>)
    (<Cell 'student'.A2>, <Cell 'student'.B2>, <Cell 'student'.C2>, <Cell 'student'.D2>)
    (<Cell 'student'.A3>, <Cell 'student'.B3>, <Cell 'student'.C3>, <Cell 'student'.D3>)
    (<Cell 'student'.A4>, <Cell 'student'.B4>, <Cell 'student'.C4>, <Cell 'student'.D4>)
    
  • 按行遍历值
    In [2]: for row in ws.values:
       ...:     print(row)
    # row为每行数据的元组,values为生成器
    ('no', 'name', 'chinese', 'math')
    (1, 'duke', 97, 88)
    (2, 'park', 95, 89)
    (3, 'sam', 96, 87)
    
  • 限定范围按行遍历cell对象,iter_rows迭代器
    In [3]: for row in ws.iter_rows(min_row=2,max_row=3,min_col=2,max_col=3):
        ...:     print(row)
    # row为限定数据
    (<Cell 'student'.B2>, <Cell 'student'.C2>)
    (<Cell 'student'.B3>, <Cell 'student'.C3>)
    

3.4 单元格对象Cell

  • 单元格对象属性
    写法解释
    ws[‘B2’]获取cell对象,例如<Cell ‘student’.B2>,ws(row=2, column=2) 效果同
    cell.row获取行数2,cell.column获取列数
    cell.value获取单元格值'duke'
    cell.coordinate获取单元格坐标'B2'
  • 通过cell对象获取值
    In [1]: for row in ws.rows:
        ...:     scores = [cell.value for cell in row]
        ...:     print(scores)
    # 跟3.3.2节按行遍历值对比
    ['no', 'name', 'chinese', 'math']
    [1, 'duke', 97, 88]
    [2, 'park', 95, 89]
    [3, 'sam', 96, 87]
    
  • 工作簿到单元格数据
    In [1]: wb = openpyxl.load_workbook("新建表格.xlsx")
    # 链式获取与修改
    In [2]: wb['student'].cell(row=2,column=2).value='jack'
    # 有数据的表格尾部追加
    In [3]: unit = [4, 'smith', 93, 90]
    In [4]: wb['student'].append(unit)
    # 将更改保存到文件
    In [5]: wb.save("新建表格.xlsx")
    

回到总目录

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值