BeautifulSoup .prettify() 自定义缩进宽度
有没有办法为 .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 个回答
根据我的了解,这个功能并没有内置,因为有一些解决方案可以解决这个问题。
假设你在使用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类就足够实现目标了。
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))'
我自己也遇到过这个问题,处理的方法可以说是非常“黑科技”:通过后处理结果。
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.prettify
和 BeautifulSoup.prettify
,你也得在那儿做同样的事情。(你可能想创建一个通用的包装,这样就可以同时应用到这两个地方,而不是重复代码。)如果还有其他的 prettify
方法,也是同样的处理方式。