如何使用缩进将HTML漂亮地打印到文件


73

我正在使用lxml.html生成一些HTML。我想将最终结果漂亮地打印(带有缩进)到html文件中。我怎么做?

这是我迄今为止一直尝试并得到的(我对Python和lxml还是比较陌生的):

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>

Answers:


100

我最终直接使用BeautifulSoup。这就是lxml.html.soupparser用于解析HTML的东西。

BeautifulSoup有一个美化方法,可以完全按照其声明的方式进行。它使用适当的缩进和所有内容修饰HTML。

BeautifulSoup不会修复HTML,因此损坏的代码仍然损坏。但是在这种情况下,由于代码是由lxml生成的,因此HTML代码至少应在语义上正确。

在我的问题给出的示例中,我将必须这样做:

from BeautifulSoup 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

2
谢谢,但是值得一提的是js,如果对某人来说很重要,则嵌入到html中不会很美。
Vitaly Zdanevich '16

10
在版本4中,将第一行更改为from bs4 import BeautifulSoup as bs
shao.lo 16/09/29

如果您只想从字符串中美化html,请参见下面的AlexG的答案。
ErikusMaximus

请注意prettify,因为它会更改文档的语义:“由于它添加了空格(以换行符的形式),因此prettify()更改了HTML文档的含义,因此不应重新格式化。目的prettify()是帮助您直观地理解结构您使用的文档中。”
BallpointBen

36

尽管我的回答现在可能无济于事,但我将其放在此处以供将来参考。

lxml.html.tostring(),的确,尽管使用了,但仍然不能很好地打印提供的HTML pretty_print=True

然而,的“兄弟” 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>

2
pretty_print标志只调用工作时etree.tostringmethod='xml',这是默认的。因此,我们在这里处理XHTML。
lenz 2015年

7
这是一个很好的答案,因为它不使用任何外部依赖项。但是,如果包含HTML的字符串etree.tostring至少在Python 2.7.10上具有回车符,则没有任何修饰,并且返回其输入不变,至少……一旦知道,替换回车符是一件简单的事情,但是您会浪费很多时间,如果你不知道这一点。
汤姆·史威利

这很棒,因为它仅提供选项卡的解决方案。这不会以其他方式(例如BeautifulSoup解决方案)更改HTML。
EarthmeL18年

不!这就是为什么。etree.tostring会将“ <i> </ i>”缩短为“ <i />”,这是不允许的。
shrewmouse19年

22

如果将HTML作为未格式化的字符串存储在变量中html_string,则可以使用beautifulsoup4如下进行操作:

from bs4 import BeautifulSoup
print(BeautifulSoup(html_string, 'html.parser').prettify())

8

如果再添加一个依存关系不是问题,则可以使用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))

这将安装其他一些依赖项,包括beautifulsoup4
byteface

4

在引擎盖下,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则它将输出换行符。


4

我尝试了BeautifulSoupprettify和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)

3

您不可以将其通过管道发送到HTML Tidy吗?无论是从壳还是通过os.system()


我最初考虑使用HTML Tidy,但是我的代码有点古怪,而且整洁最终对它造成了破坏。决定改为使用BeautifulSoup。像魅力一样工作。
bcosynot 2011年

HTML Tidy纠正了可能破坏内容的HTML 。如果您忘记了HTML Tidy正在处理结果(我知道我在说什么),就很难找到这样的错误……
mzuther 2014年

1
除了此处的2011年评论以外,请参见此2018年问题的答案:stackoverflow.com/questions/50380799/…。“该库已损坏和/或不适用于python 3.5。” 可能会节省一些时间...
RBV

2

如果您不关心古怪的HTML风格(例如,您必须完全支持那些使用Netscpae 2.0的客户端,因此必须使用<br>而不是<br />必须这样做),则可以始终将方法更改为“ xml”,这似乎可行。这可能是lxml或libxml中的错误,但我找不到原因。


1
将方法设置为xml时,如果标签没有任何子元素,则不会生成结束标签。例如,在上述示例中,内部div将没有结束标记。我真的不知道为什么。我最终使用BeautifulSoup获得了正确的输出。
bcosynot 2011年

2

不是我的代码,我在某个地方选择了它

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.