用Python编写的HTML缩进工具

14

我正在寻找一个用Python编写的自由(指自由软件)HTML缩进器(或重新缩进器)(模块或命令行)。 我不需要使用白名单过滤HTML。 我只想缩进(或重新缩进)HTML源代码以使其更易读。 例如,假设我有以下代码:

<ul><li>Item</li><li>Item
</li></ul>

输出可能是这样的:

<ul>
    <li>Item</li>
    <li>Item</li>
</ul>

注意:我不是在寻找与非Python软件(例如用C编写的Tidy)的接口,而是要找一个100% Python脚本。

非常感谢。

5个回答

8

您可以使用内置模块xml.dom.minidomtoprettyxml函数:

>>> from xml.dom import minidom
>>> x = minidom.parseString("<ul><li>Item</li><li>Item\n</li></ul>")
>>> print x.toprettyxml()
<?xml version="1.0" ?>
<ul>
    <li>
        Item
    </li>
    <li>
        Item
    </li>
</ul>

如何删除 <?xml version="1.0" ?> 这一行? - Arbaz Siddiqui
你可以直接移除第一行 '\n'.join(x.toprettyxml().splitlines()[1:])(虽然不是最好的解决方案,但可以完成工作)。 - Elisha
要删除头部,其中 header='<?xml version="1.0" encoding="utf-8"?>'(这是当前默认的xml头),可以使用以下代码:re.sub(re.escape(header), '', xml, flags=re.IGNORECASE | re.MULTILINE).strip()(先导入import re)。 - PatrickT
为了删除 <?xml version="1.0" ?>,我使用了 html = html[23:-1]。这也会去掉末尾的空行。 - Harley
为了删除空白行,使用.strip()更加安全。 - Elisha
避免使用额外的库是一个很好的解决方案,但是XML解析器在处理不规范的HTML时更加脆弱,可能会出现诸如xml.parsers.expat.ExpatError: not well-formed (invalid token)的错误--清理源代码可能需要更多的努力,请参考https://dev59.com/7lUM5IYBdhLWcg3wdP5O。 - Mark Chackerian

7

使用BeautifulSoup

有许多方法可以使用BeautifulSoup模块及其prettify函数。以下是一些示例,可帮助您入门。

使用命令行

$ python -m BeautifulSoup < somefile.html > prettyfile.html

在VIM中(手动)

如果您不想将文件写回磁盘,那么您不必这样做,但我包括了一个步骤,可以获得与命令行示例相同的效果。

$ vi somefile.html
:!python -m BeautifulSoup < %
:w prettyfile.html

在VIM中定义按键映射

在~/.vimrc文件中进行定义:

nmap =h !python -m BeautifulSoup < %<CR>

然后,当您在vim中打开文件并且需要美化时

$vi somefile.html
=h
:w prettyfile.html

再次强调,保存美化是可选的。

Python Shell

$ python
>>> from BeautifulSoup import BeautifulSoup as parse_html_string
>>> from os import path
>>> uglyfile = path.abspath('somefile.html')
>>> path.isfile(uglyfile)
True
>>> prettyfile = path.abspath(path.join('.', 'prettyfile.html'))
>>> path.exists(prettyfile)
>>> doc = None
>>> with open(uglyfile, 'r') as infile, open(prettyfile, 'w') as outfile:
...     # Assuming very simple case
...     htmldocstr = infile.read()
...     doc = parse_html_string(htmldocstr)
...     outfile.write(doc.prettify())

# That's it; you can manually manipulate the dom too though
>>> scripts = doc.findAll('script')
>>> meta = doc.findAll('meta')
>>> print doc.prettify()
[imagine beautiful html here]

>>> import jsbeautifier
>>> print jsbeautifier.beautify(script.string)
[imagine beautiful script here]
>>> 

非常感谢您的回答。 - jep

4

还有一个html5print模块,以下是其主要特点:

  • 将HTML、嵌入式CSS和JavaScript进行漂亮的打印处理
  • 对纯CSS和JavaScript进行漂亮的打印处理
  • 尝试修复碎片化的HTML5
  • 尝试修复具有破损的unicode编码的HTML
  • 尝试猜测文档的编码,在某些情况下能够将8位字节码转换回正确的UTF-8格式
  • 支持Python 2和3

这里使用了bs4,具体实现请参考其他答案中的bs4 prettify。 - Rod Maniego

3

BeautifulSoup有一个名为prettify的函数,可以实现这个功能。 参见这个问题


2
除了它并没有这样做。它每个缩进级别只给出1个空格,而且这是不可参数化的 - OP想要每个级别4个空格。它也不允许您指定不想缩进的标记,例如<a>,或内联元素如<b>,<i>,<strong>等。它基本上没有任何参数化功能。这就是为什么你会看到这么多关于这个问题的提问,已经有十年了。 - smci

1

Here's my pure python solution:

from xml.dom.minidom import parseString as string_to_dom

def prettify(string, html=True):
    dom = string_to_dom(string)
    ugly = dom.toprettyxml(indent="  ")
    split = list(filter(lambda x: len(x.strip()), ugly.split('\n')))
    if html:
        split = split[1:]
    pretty = '\n'.join(split)
    return pretty

def pretty_print(html):
    print(prettify(html))

当应用于您的HTML块时:
html = """<ul><li>Item</li><li>Item</li></ul>"""
pretty_print(html)

我得到:

<ul>
  <li>Item</li>
  <li>Item</li>
</ul>

当我使用来自lxml html/etree输出的utf-8字符串时,我遇到了一个"xml.parsers.expat.ExpatError: not well-formed (invalid token): line 1, column 110"错误。 - Rod Maniego
@RodManiego 同样的问题,你解决了吗? - Cavalex

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