BeautifulSoup .prettify() 自定义缩进宽度

38 投票
4 回答
15146 浏览
提问于 2025-04-17 19:40

有没有办法为 .prettify() 函数定义自定义的缩进宽度?从我看到的源代码来看 -

def prettify(self, encoding=None, formatter="minimal"):
    if encoding is None:
        return self.decode(True, formatter=formatter)
    else:
        return self.encode(encoding, True, formatter=formatter)

似乎没有办法指定缩进宽度。我觉得这是因为 decode_contents() 函数里有这么一行 -

s.append(" " * (indent_level - 1))

它的长度固定为1个空格!(为什么会这样!!) 我试着指定 indent_level=4,结果却变成了这个 -

    <section>
     <article>
      <h1>
      </h1>
      <p>
      </p>
     </article>
    </section>

看起来真是太傻了。:|

现在,我可以想办法解决这个问题,但我只是想确认一下我是不是漏掉了什么。因为这应该是一个基本功能。:-/

如果你有更好的方法来美化HTML代码,请告诉我。

4 个回答

6

根据我的了解,这个功能并没有内置,因为有一些解决方案可以解决这个问题。

假设你在使用BeautifulSoup 4,这里有我想到的解决办法:

可以直接写死在代码里。这种方法改动很小,如果你不需要在不同情况下缩进不同,这样做是可以的:

myTab = 4 # add this
if pretty_print:
   # space = (' ' * (indent_level - 1))
    space = (' ' * (indent_level - myTab))
    #indent_contents = indent_level + 1
    indent_contents = indent_level + myTab 

不过,之前的解决方案还有一个问题,就是文本内容的缩进可能不够一致,但看起来还是不错的。如果你需要一个更灵活和一致的解决方案,可以修改一下类。

找到prettify函数并进行如下修改(这个函数在element.py的Tag类里):

#Add the myTab keyword to the functions parameters (or whatever you want to call it), set it to your preferred default.
def prettify(self, encoding=None, formatter="minimal", myTab=2): 
    Tag.myTab= myTab # add a reference to it in the Tag class
    if encoding is None:
        return self.decode(True, formatter=formatter)
    else:
        return self.encode(encoding, True, formatter=formatter)

然后向上滚动到Tag类里的decode方法,进行以下更改:

if pretty_print:
    #space = (' ' * (indent_level - 1))
    space = (' ' * (indent_level - Tag.myTab))
    #indent_contents = indent_level + Tag.myTab 
    indent_contents = indent_level + Tag.myTab

接着去Tag类里的decode_contents方法,做这些更改:

#s.append(" " * (indent_level - 1))
s.append(" " * (indent_level - Tag.myTab))

现在,使用BeautifulSoup('<root><child><desc>Text</desc></child></root>').prettify(myTab=4)将会返回:

<root>
    <child>
        <desc>
            Text
        </desc>
    </child>
</root>

**不需要修改BeautifulSoup类,因为它继承了Tag类。只修改Tag类就足够实现目标了。

13

Beautiful Soup 有一些叫做 输出格式化器 的工具。bs4.formatter.HTMLFormatter 这个工具可以让你设置 indent,也就是缩进。

>>> import bs4
>>> s = '<section><article><h1></h1><p></p></article></section>'
>>> formatter = bs4.formatter.HTMLFormatter(indent=2)
>>> print(bs4.BeautifulSoup(s, 'html.parser').prettify(formatter=formatter))
<section>
  <article>
    <h1>
    </h1>
    <p>
    </p>
  </article>
</section>

你还可以通过命令行使用它,配合 pyfil 工具(比如说,可以和 Geany 的“发送选中内容到”功能结合使用):

pyfil 'bs4.BeautifulSoup(stdin, "html.parser").prettify(formatter=bs4.formatter.HTMLFormatter(indent=2))'
27

我自己也遇到过这个问题,处理的方法可以说是非常“黑科技”:通过后处理结果。

r = re.compile(r'^(\s*)', re.MULTILINE)
def prettify_2space(s, encoding=None, formatter="minimal"):
    return r.sub(r'\1\1', s.prettify(encoding, formatter))

其实,我在这个类里把 prettify_2space 替换成了 prettify。这并不是解决问题的关键,但我们还是这样做吧,并且把缩进的宽度设为一个参数,而不是死死写成2:

orig_prettify = bs4.BeautifulSoup.prettify
r = re.compile(r'^(\s*)', re.MULTILINE)
def prettify(self, encoding=None, formatter="minimal", indent_width=4):
    return r.sub(r'\1' * indent_width, orig_prettify(self, encoding, formatter))
bs4.BeautifulSoup.prettify = prettify

所以:

x = '''<section><article><h1></h1><p></p></article></section>'''
soup = bs4.BeautifulSoup(x)
print(soup.prettify(indent_width=3))

… 结果是:

<html>
   <body>
      <section>
         <article>
            <h1>
            </h1>
            <p>
            </p>
         </article>
      </section>
   </body>
</html>

显然,如果你想同时修改 Tag.prettifyBeautifulSoup.prettify,你也得在那儿做同样的事情。(你可能想创建一个通用的包装,这样就可以同时应用到这两个地方,而不是重复代码。)如果还有其他的 prettify 方法,也是同样的处理方式。

撰写回答