30

有没有办法为.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

3 回答 3

26

实际上,我自己以最骇人听闻的方式处理了这个问题:通过对结果进行后处理。

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_2spaceprettify课堂上打了补丁。这对解决方案来说不是必需的,但无论如何让我们这样做,并将缩进宽度作为参数而不是将其硬编码为 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方法,同样的处理。

于 2013-03-20T01:06:34.630 回答
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 类足以实现目标。

于 2013-03-20T00:59:57.057 回答
3

这是一种在不干预原始函数等的情况下增加缩进的方法。创建以下函数:

# Increase indentation of 'text' by 'n' spaces
def add_indent(text,n):
  sp = " "*n
  lsep = chr(10) if text.find(chr(13)) == -1 else chr(13)+chr(10)
  lines = text.split(lsep)
  for i in range(len(lines)):
    spacediff = len(lines[i]) - len(lines[i].lstrip())
    if spacediff: lines[i] = sp*spacediff + lines[i] 
  return lsep.join(lines)

然后使用上述函数转换您获得的文本:

x = '''<section><article><h1></h1><p></p></article></section>'''
soup = bs4.BeautifulSoup(x, 'html.parser')  # I don't know if you need 'html.parser'
text = soup.prettify()                      # I do, otherwise I get a warning
text = add_indent(text,1) # Increase indentation by 1 space 
print(text)
'''
Output:
<html>
  <body>
    <section>
      <article>
        <h1>
        </h1>
        <p>
        </p>
      </article>
    </section>
  </body>
</html>
'''
于 2020-07-27T06:08:57.557 回答