BeautifulSoup Prettify пользовательская новая опция строки

Я использую BeautifulSoup для создания файлов xml.

Кажется, что мои два варианта: 1) без форматирования, т.е.

<root><level1><level2><field1>val1</field1><field2>val2</field2><field3>val3</field3></level2></level1></root>

или 2) с prettify, т.е.

<root>
 <level1>
  <level2>
   <field1>
    val1
   </field1>
   <field2>
    val2
   </field2>
   <field3>
    val3
   </field3>
  </level2>
 </level1>
</root>

Но я бы предпочел, чтобы это выглядело так:

<root>
    <level1>
        <level2>
            <field1>val1</field1>
            <field2>val2</field2>
            <field3>val3</field3>
        </level2>
    </level1>
</root>

Я понимаю, что могу взломать bs4 для достижения такого результата, но я хотел бы услышать, существуют ли какие-либо варианты.

Меня меньше беспокоит отступ в 4 пробела (хотя это было бы неплохо) и больше беспокоит новая строка после любых закрывающих тегов или между двумя открывающими тегами. Я также заинтригован, есть ли название для этого способа форматирования, так как он кажется мне наиболее разумным.


person teebagz    schedule 16.10.2018    source источник
comment
stackoverflow.com/questions/ 15509397/ помочь вам.   -  person MikiBelavista    schedule 16.10.2018
comment
спасибо @MikiBelavista, но этот вопрос больше сосредоточен на размере отступа, где меня больше всего беспокоят новые строки, в частности, наличие новых строк только после закрывающих тегов и между двумя открывающими тегами.   -  person teebagz    schedule 16.10.2018
comment
@TommyGaboreau Если сделать простое html.parser можно, посмотрите мой ответ.   -  person Andrej Kesely    schedule 20.06.2019


Ответы (1)


Вы можете сделать простой html.HTMLParser для достижения желаемого:

from bs4 import BeautifulSoup
from html import escape
from html.parser import HTMLParser

data = '''<root><level1><level2><field1>val1</field1><field2>val2</field2><field3>val3</field3></level2></level1></root>'''

class MyHTMLParser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.__t = 0
        self.lines = []
        self.__current_line = ''
        self.__current_tag = ''

    @staticmethod
    def __attr_str(attrs):
        return ' '.join('{}="{}"'.format(name, escape(value)) for (name, value) in attrs)

    def handle_starttag(self, tag, attrs):
        if tag != self.__current_tag:
            self.lines += [self.__current_line]

        self.__current_line = '\t' * self.__t + '<{}>'.format(tag + (' ' + self.__attr_str(attrs) if attrs else ''))
        self.__current_tag = tag
        self.__t += 1

    def handle_endtag(self, tag):
        self.__t -= 1
        if tag != self.__current_tag:
            self.lines += [self.__current_line]
            self.lines += ['\t' * self.__t + '</{}>'.format(tag)]
        else:
            self.lines += [self.__current_line + '</{}>'.format(tag)]

        self.__current_line = ''

    def handle_data(self, data):
        self.__current_line += data

    def get_parsed_string(self):
        return '\n'.join(l for l in self.lines if l)


parser = MyHTMLParser()

soup = BeautifulSoup(data, 'lxml')
print('BeautifulSoup prettify():')
print('*' * 80)
print(soup.root.prettify())

print('custom html parser:')
print('*' * 80)
parser.feed(str(soup.root))
print(parser.get_parsed_string())

Отпечатки:

BeautifulSoup prettify():
********************************************************************************
<root>
 <level1>
  <level2>
   <field1>
    val1
   </field1>
   <field2>
    val2
   </field2>
   <field3>
    val3
   </field3>
  </level2>
 </level1>
</root>
custom html parser:
********************************************************************************
<root>
    <level1>
        <level2>
            <field1>val1</field1>
            <field2>val2</field2>
            <field3>val3</field3>
        </level2>
    </level1>
</root>
person Andrej Kesely    schedule 20.06.2019
comment
для моей цели улучшить xml-вывод документа docx ваш код работал отлично, за исключением того, что я заменил суп.рут на суп. - person El_1988; 18.11.2020