我正在使用 lxml.html 生成一些 HTML。我想将我的最终结果漂亮地打印(带缩进)到一个 html 文件中。我该怎么做?
这就是我目前尝试得到的
import lxml.html as lh
from lxml.html import builder as E
sliderRoot=lh.Element("div", E.CLASS("scroll"), style="overflow-x: hidden; overflow-y: hidden;")
scrollContainer=lh.Element("div", E.CLASS("scrollContainer"), style="width: 4340px;")
sliderRoot.append(scrollContainer)
print lh.tostring(sliderRoot, pretty_print = True, method="html")
如您所见,我正在使用
pretty_print=True
属性。我认为这会给出缩进代码,但它并没有真正帮助。这是输出:
<div style="overflow-x: hidden; overflow-y: hidden;" class="scroll"><div style="width: 4340px;" class="scrollContainer"></div></div>
我最终直接使用了BeautifulSoup。这是 lxml.html.soupparser 用于解析 HTML 的东西。
BeautifulSoup 有一个美化方法,它完全按照它说的做。它用适当的缩进和一切美化 HTML。
BeautifulSoup 不会修复 HTML,因此损坏的代码仍然会损坏。但在这种情况下,由于代码是由 lxml 生成的,因此 HTML 代码至少在语义上应该是正确的。
在我的问题给出的例子中,我将不得不这样做:
from bs4 import BeautifulSoup as bs
root = lh.tostring(sliderRoot) #convert the generated HTML to a string
soup = bs(root) #make BeautifulSoup
prettyHTML = soup.prettify() #prettify the html
虽然我的回答现在可能没有帮助,但我把它放在这里以供将来其他人参考。
lxml.html.tostring()
,事实上,尽管有pretty_print=True
.,但并不能很好地打印提供的 HTML
然而,
lxml.html
- lxml.etree
的“兄弟姐妹”让它运作良好。
所以可以按如下方式使用它:
from lxml import etree, html
document_root = html.fromstring("<html><body><h1>hello world</h1></body></html>")
print(etree.tostring(document_root, encoding='unicode', pretty_print=True))
输出是这样的:
<html>
<body>
<h1>hello world</h1>
</body>
</html>
如果将 HTML 存储为未格式化的字符串,在变量
html_string
中,可以使用 beautifulsoup4 完成,如下所示:
from bs4 import BeautifulSoup
print(BeautifulSoup(html_string, 'html.parser').prettify())
如果再添加一个依赖没有问题,你可以使用 html5print 包。与其他解决方案相比的优势在于,它还美化了嵌入在 HTML 文档中的 CSS 和 Javascript 代码。
要安装它,请执行:
pip install html5print
然后,您可以将它用作命令:
html5-print ugly.html -o pretty.html
或作为 Python 代码:
from html5print import HTMLBeautifier
html = '<title>Page Title</title><p>Some text here</p>'
print(HTMLBeautifier.beautify(html, 4))
我尝试了 BeautifulSoup 的
prettify
和 html5print 的 HTMLBeautifier
解决方案,但由于我使用 yattag 来生成 HTML,因此使用它的 indent
函数似乎更合适,它会产生很好的缩进输出。
from yattag import indent
rawhtml = "String with some HTML code..."
result = indent(
rawhtml,
indentation = ' ',
newline = '\r\n',
indent_text = True
)
print(result)
在幕后,
lxml
使用 libxml2
将树序列化回字符串。以下是确定是否在关闭标签后附加换行符的相关代码片段:
xmlOutputBufferWriteString(buf, ">");
if ((format) && (!info->isinline) && (cur->next != NULL)) {
if ((cur->next->type != HTML_TEXT_NODE) &&
(cur->next->type != HTML_ENTITY_REF_NODE) &&
(cur->parent != NULL) &&
(cur->parent->name != NULL) &&
(cur->parent->name[0] != 'p')) /* p, pre, param */
xmlOutputBufferWriteString(buf, "\n");
}
return;
所以如果一个节点是一个元素,不是内联标签并且后面跟着一个兄弟节点(
cur->next != NULL
)并且不是p, pre, param
之一那么它将输出一个换行符。
你不能把它导入HTML Tidy吗?从外壳或通过
os.system()
.
如果您不关心古怪的 HTML(例如,您必须绝对支持那些使用 Netscpae 2.0 的客户端,因此必须使用
<br>
而不是 <br />
),您可以随时将方法更改为“xml ",这似乎有效。这可能是 lxml 或 libxml 中的错误,但我找不到原因。
不是我的代码,我在某处捡到的
def indent(elem, level=0):
i = '\n' + level * ' '
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + ' '
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
indent(elem, level+1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
我用它:
indent(page)
tostring(page)
这很粗糙而且不是很健壮,但它会平衡示例 html 字符串,而无需使用任何非标准库
import re
html = """
<A value="X"><B value="X">
<A value="X"><B value="X">
some random text
</B></A><C />
some random text
</B></A><C />
"""
rx_al = r"(<[\/A-Za-z]+[^>]*>)"
rx_op = r"<([A-Za-z]+)[^\/]+>"
rx_cl = r"</([A-Za-z]+)>"
# self-closing not needed
#rx_sc = r"<([A-Za-z]+).* \/>"
matches = re.findall(rx_al, html, flags=re.M)
def lookup_key(match, indent):
return f"{match[0]}:{indent}"
def balance(nodes):
builder = []
indent = 0
lookup = {}
for node in nodes:
is_open = re.match(rx_op, node)
is_close = re.match(rx_cl, node)
padl = " " * indent
if is_open:
k = lookup_key(is_open, indent)
lookup[k] = node
indent += 2
elif is_close:
for i in range(0, indent, -2):
if lookup.pop(lookup_key(is_close, i), None):
break
indent -= 2
padl = padl[:-2]
builder.append(padl + node)
return "\n".join(builder)
print(balance(matches))
将产生:
<A value="X">
<B value="X">
<A value="X">
<B value="X">
</B>
</A>
<C />
</B>
</A>
<C />