使用Jinja2模板压缩Flask应用程序的HTML输出

25

是否有Flask或Jinja2的配置标志/扩展来在渲染模板后自动缩小HTML输出?


3
你看过https://github.com/mitsuhiko/jinja2-htmlcompress吗? - Sean Vieira
@SeanVieira 它实际上删除了很多有用的空格.. 所以它正在破坏你的HTML.. - Lipis
8个回答

31

我发现了一种更好的方法来实现这一点。您可以使用此方法对所有页面进行压缩:

from flask import Flask
from htmlmin.main import minify

app = Flask(__name__)


@app.after_request
def response_minify(response):
    """
    minify html response to decrease site traffic
    """
    if response.content_type == u'text/html; charset=utf-8':
        response.set_data(
            minify(response.get_data(as_text=True))
        )

        return response
    return response

在Python2.7上按预期完美运行,但在Python2.6上表现奇怪。 抛出“ValueError: zero length field name in format”异常。 - 25mhz
1
感谢您提供的优秀代码片段。我已经对其进行了修改,使其能够美化输出,而非缩小输出:http://stackoverflow.com/a/43606937/2648551 - colidyre
1
这难道不会在每个请求时都使输出最小化吗?它是否会更改模板的缓存版本(或在保存缓存之前对其进行最小化)? - jeromej

10

不幸的是,它的 setup.py 文件仍然需要 Django。 - H.D.
3
经过这次更新,不再这样了。 - alexcasalboni
2
有没有“部署时”解决方案,类似于许多js项目的做法?大多数模板和静态数据每次和每个请求都不需要进行缩小。如果能够在setup.py期间尽可能地缩小所有内容,那就太好了。 - Dan M.

8

我使用以下修饰器

import bs4
import functools
import htmlmin


def prettify(route_function):
    @functools.wraps(route_function)
    def wrapped(*args, **kwargs):
        yielded_html = route_function(*args, **kwargs)
        soup = bs4.BeautifulSoup(yielded_html, 'html.parser')
        return soup.prettify()

    return wrapped

def uglify(route_function):
    @functools.wraps(route_function)
    def wrapped(*args, **kwargs):
        yielded_html = route_function(*args, **kwargs)
        minified_html = htmlmin.minify(yielded_html)
        return minified_html

    return wrapped

只需像这样简单地包装默认的render_template函数即可:
if app.debug:
    flask.render_template = prettify(flask.render_template)
else:
    flask.render_template = uglify(flask.render_template)

这样做的好处是自动添加到缓存中,因为我们实际上没有触及app.route。

遗憾的是,您仍然需要在每个视图文件中应用导入它...我曾希望您只需更改引擎,所有输出都将被压缩。 - jeromej
为什么你使用自己的装饰器而不是已经存在于 htmlmin 中的装饰器? - bfontaine
1
@bfontaine 说实话,我是在6年前刚开始上大学时写下这个答案的。我得假设当时我没有在他们的文档中看到它,或者我觉得自己很聪明之类的。但我认为我给出的答案并不是非常糟糕的。 - Ethan McCue
还有 @jeromej,虽然迟了五年,但这个答案确实会影响整个引擎,因为它替代了 flask.render_template。 - Ethan McCue

6
我已经编写了一个Flask扩展来实现这个目的。您可以使用pip install flask-htmlmin进行安装,源代码可在https://github.com/hamidfzm/Flask-HTMLmin上找到。希望它能对您有所帮助。

从“我想知道如何在Flask中轻松压缩HTML”这个想法开始,到“当然有人在SO上问过这个问题”,再到“啊,自然有一个库”,最终得出这个答案:“神奇的hamidfzm编写了一个Flask插件,在两行代码中完成所有操作”。 - kontur

5

使用装饰器。

from htmlmin.decorator import htmlmin

@htmlmin
def home():
...

或者你可以直接使用:

re.sub(r'>\s+<', '><', '<tag>   </tag>') # results '<tag></tag>'

1
你能提供一些参考链接吗? - everton
小心使用正则表达式,它可能会以明显的方式破坏你的HTML。将字符串a <b>big</b> <i>fat</i> <s>edit</s> here压缩后,输出结果为a <b>big</b><i>fat</i><s>edit</s> here--空格已被删除。 - smitelli
标准中省略了字符之间的空格。这就是为什么在Django中有**{% spaceless %}标签和htmlminminify**方法的原因之一。 - Andrey Shipilov
这个方法能够使用缓存吗?还是只有Ethan McCue的答案可行?(我还没有深入研究,但也许你已经知道答案了) - jeromej
当然会。因为bs4,伊桑的方法有些过度了。 - Andrey Shipilov

3
为了扩展@olly_uk的答案和@Alexander的评论的实用性,看起来django-htmlmin扩展现在被设计用于除Django之外的框架。
这里的文档中,您可以手动在Flask视图中使用html_minify函数,如下所示:
from flask import Flask
from htmlmin.minify import html_minify

app = Flask(__name__)

@app.route('/')
def home():
    rendered_html = render_template('home.html')
    return html_minify(rendered_html)

这个是在模板运行时还是在编译时生效? - user_78361084
1
@llamawithabowlcut,html_minify在模板在运行时被渲染后调用。如果问题背后的关注点是性能,我也会尽可能地将其与Flask缓存(在我的情况下是Flask-cacheify)相结合。如果不可能,那么是的,为每个视图提供服务时调用html_minify可能相对昂贵。 - Bletch
我可以使用re模块来完成这个任务:return sub(r'\s{2,}|[\r\n]', '', render_template('blah.html'))... 什么使得minify特别? - Vasili Syrakis
@Sasili Syrakis - 对于OP的用例,我不确定是否有任何东西构成“特殊”,但它是注释感知的 - 允许您在缩小的HTML中包含或排除注释。它还带有一个方便的装饰器,有一个静态HTML文件的命令行版本,并允许您按路径包含或排除文件。*我没有尝试过这些功能与Flask一起使用。 - Bletch

1
修改@Bletch的答案以适应最新版本的htmlmin。
from flask import Flask
import htmlmin

app = Flask(__name__)

@app.route('/')
def home():
    rendered_html = render_template('home.html')
    return htmlmin.minify(rendered_html)

https://htmlmin.readthedocs.io/en/latest/quickstart.html

最小化的HTML仍然会在标签之间保留一些空格。如果我们想要删除它,那么在渲染模板时需要添加remove_empty_space=True属性。
return htmlmin.minify(rendered_html, remove_empty_space =True)

https://htmlmin.readthedocs.io/en/latest/reference.html


0

在2022年,我发现使用minify-html包取得了更好的成功:https://github.com/wilsonzlin/minify-html

minify_html.minify(
    """
    html here
    """,
    minify_js=True, 
    minify_css=True,
    remove_processing_instructions=True,
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接