html5tagger 快速上手#

安装:

pip install html5tagger

html5tagger 提供了两个 HTML 生成的起点:E 用于创建空的构建器来生成 HTML 片段,或者 Document 用于生成带有 DOCTYPE 声明的完整 HTML 文档。两者都会产生 Builder 对象,以防你需要它进行类型注解。

通过点表示法创建片段并添加标签:

E.p("Powered by:").br.a(href="...")("html5tagger")

生成:

<p>Powered by:<br><a href="...">html5tagger</a>

html5tagger 简单示例#

from html5tagger import Document, E

创建 document

doc = Document(
    E.TitleText_, # 第一个参数是用于 `<title>`,添加变量TitleText。
    lang="en", # 关键字参数用于 `<html>` 属性。
    # 只需列出你需要的资源,无需记住 link/script标签。
    _urls=[ "style.css", "favicon.png", "manifest.json" ]
)

大写名称是模板变量。你可以在之后修改它们:

print(doc.Head_)
<!DOCTYPE html><html lang=en><meta charset="utf-8"><title></title><link href="style.css" rel=stylesheet><link href="favicon.png" rel=icon type="image/png"><link href="manifest.json" rel=manifest>

进入 <h1> 并同时更新 <title>

print(doc.h1.TitleText_("Demo") )
<!DOCTYPE html><html lang=en><meta charset="utf-8"><title>Demo</title><link href="style.css" rel=stylesheet><link href="favicon.png" rel=icon type="image/png"><link href="manifest.json" rel=manifest><h1>Demo</h1>

这一直是 DOM 和其他此类生成器的难题:

doc.p("A paragraph with ").a("a link", href="/files")(" and ").em("formatting")
Demo

Demo

A paragraph with a link and formatting

对于复杂的嵌套(通常不需要)使用 with

with doc.table(id="data"):
    doc.tr.th("First").th("Second").th("Third")
    doc.TableRows_

在模板变量中添加一些东西:

print(doc.Head._script("console.log('</script> escaping is weird')"))
<script>console.log('<\/script> escaping is weird')</script>
table = doc.TableRows
for row in range(10):
    table.tr
    for col in range(3):
        table.td(row * col)
doc
Demo

Demo

A paragraph with a link and formatting

FirstSecondThird
000
012
024
036
048
0510
0612
0714
0816
0918

或者删除我们刚刚添加的表格数据:

doc.TableRows = None
doc
Demo

Demo

A paragraph with a link and formatting

FirstSecondThird

你可以使用 str(doc) 来获取 HTML 代码,直接使用 doc 通常也能达到预期效果(例如提供 HTML 响应)。Jupyter Notebooks 将其渲染为 HTML。对于调试,使用 repr(doc),其中模板变量是可见的:

print(doc)
<!DOCTYPE html><html lang=en><meta charset="utf-8"><title>Demo</title><link href="style.css" rel=stylesheet><link href="favicon.png" rel=icon type="image/png"><link href="manifest.json" rel=manifest><script>console.log('<\/script> escaping is weird')</script><h1>Demo</h1><p>A paragraph with <a href="/files">a link</a> and <em>formatting</em><table id=data><tr><th>First<th>Second<th>Third</table>

实际的 HTML 输出是类似的。文档中没有添加任何空白,除非内容包含换行符,否则都在同一行。您可能会注意到,body 和其他熟悉的标签都不见了,转义也很少。这就是HTML5:文档符合标准,且少了很多冗余。

html5tagger 模板#

使用模板变量构建一次文档,在渲染时只更新动态部分,以获得更快的性能。通过 doc.TitleText 访问模板变量,并在标签名后的括号中添加内容。标签名末尾的下划线表示该标签被添加到文档中,并且可以在括号中有内容,但同一行上任何进一步的标签都会进入原始文档,而不是模板。

html5tagger 嵌套#

在 HTML5 中,像 <p> 这样的元素不需要任何闭合标签,因此我们可以不断添加内容而不必担心何时应该关闭。对于可选或禁止使用闭合标签的元素,此模块不使用闭合标签。

当您向一个元素添加内容或添加另一个标签时,该元素会自动闭合。仅仅设置属性并不会关闭一个元素。如果后续的任何内容都不应该在它内部,则可以使用(None)来关闭空元素,例如 doc.script(None, src="...")

对于像 <table><ul> 这样的元素,您可以使用 with 块,传递子片段参数,或添加一个模板变量。与添加另一个标签不同,添加模板不会关闭其前面的标签,而是将变量放在任何打开的元素内部。

with doc.ul:  # Nest using with
    doc.li("Write HTML in Python")
    doc.li("Simple syntax").ul(id="inner").InnerList_  # Nest using template
    doc.li("No need for brackets or closing tags")
    doc.ul(E.li("Easy").li("Peasy"))  # Nest using (...)

html5tagger Escaping#

所有内容和属性都会自动转义。例如,我们可以将整个文档放入 iframesrcdoc 属性中,这里只应用了最少但必要的转义。使用自定义方法 _script_style_comment 进行相应的内联格式,以遵循它们的自定义转义规则。

doc = Document("Escaping & Context")
doc._style('h1::after {content: "</Style>"}').h1("<Escape>")
doc._comment("All-->OK")
doc.iframe(srcdoc=Document().p("&amp; is used for &"))
Escaping & Context

<Escape>

名称混淆和布尔属性#

名称末尾的下划线会被忽略,因此尽管 class_for_ 等属性是 Python 中的保留字,也可以使用。其他下划线会转换为连字符。

备注

上述仅适用于 HTML 元素和属性,但模板占位符仅使用末尾的下划线来表示它应该放在文档上,而不是被获取以供使用。

布尔值会转换为简短的属性。

E.input(type="checkbox", id="somebox", checked=True).label(for_="somebox", aria_role="img")("🥳")

预格式化的 HTML#

所有内容都会自动转义,除非它提供了 __html__ 方法,该方法返回 HTML 格式的字符串。同样,此模块的构建器对象也暴露了 __html___repr_html_ 访问器,允许它们在 Jupyter Notebooks 和遵循此约定的各种其他系统中渲染为 HTML。

任何预格式化的 HTML 都可以用 html5tagger.HTML(string_of_html) 包裹,以避免在包含在文档中时被转义,因为 HTML 类有这些访问器。

小技巧

不要将 HTML() 用于文本,特别是不要用于用户发送的可能包含您不打算作为 HTML 执行的 HTML 的消息。

html5tagger 性能#

%timeit str(Document("benchmarking", lang="en", _urls=("foo.js", "bar.js")))
24.6 μs ± 38.8 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

Jinja2 从内存模板渲染类似的文档大约需要 10 微秒,但它不需要格式化任何 HTML。当模板与 html5tagger 类似地使用时,渲染时间降至约 4 微秒。

在上面的基准测试中,html5tagger 从头开始创建整个文档,一次一个元素和一个属性。除非您正在动态创建非常大的文档,否则这应该足够快了。