Как красиво напечатать HTML в файл с отступом

Я использую lxml.html для создания некоторого HTML. Я хочу красиво напечатать (с отступом) мой окончательный результат в html-файле. Как мне это сделать?

Это то, что я пробовал и получил до сих пор (я относительно новичок в Python и lxml):

import lxml.html as lh
from lxml.html import builder as E
sliderRoot=lh.Element("div", E.CLASS("scroll"), style="overflow-x: hidden; overflow-y: hidden;")
scrollContainer=lh.Element("div", E.CLASS("scrollContainer"), style="width: 4340px;")
sliderRoot.append(scrollContainer)
print lh.tostring(sliderRoot, pretty_print = True, method="html")

Как видите, я использую атрибут pretty_print=True. Я думал, что это даст код с отступом, но это не очень помогает. Это вывод:

<div style="overflow-x: hidden; overflow-y: hidden;" class="scroll"><div style="width: 4340px;" class="scrollContainer"></div></div>


person bcosynot    schedule 27.05.2011    source источник


Ответы (9)


В итоге я использовал BeautifulSoup напрямую. Это то, что lxml.html.soupparser использует для разбора HTML.

BeautifulSoup имеет метод prettify, который делает именно то, что он говорит. Он украшает HTML правильными отступами и всем остальным.

BeautifulSoup НЕ исправит HTML, поэтому сломанный код останется сломанным. Но в этом случае, поскольку код генерируется lxml, HTML-код должен быть хотя бы семантически правильным.

В примере, приведенном в моем вопросе, мне придется сделать это:

from BeautifulSoup import BeautifulSoup as bs
root = lh.tostring(sliderRoot) #convert the generated HTML to a string
soup = bs(root)                #make BeautifulSoup
prettyHTML = soup.prettify()   #prettify the html
person bcosynot    schedule 29.05.2011
comment
Спасибо, но стоит упомянуть, что js встроенный в html не будет претифицирован, если это кому-то важно. - person Vitaly Zdanevich; 15.09.2016
comment
В версии 4 измените первую строку на from bs4 import BeautifulSoup as bs - person shao.lo; 29.09.2016
comment
Если вы просто хотите преобразовать html из строки, см. ответ AlexG ниже. - person ErikusMaximus; 14.08.2020
comment
Будьте осторожны с prettify, так как он изменяет семантику документа: поскольку он добавляет пробелы (в виде новых строк), prettify() изменяет значение HTML-документа и не должен использоваться для его переформатирования. Цель prettify() — помочь вам визуально понять структуру документов, с которыми вы работаете. - person BallpointBen; 23.12.2020

Хотя мой ответ может быть бесполезен сейчас, я оставляю его здесь, чтобы в будущем он мог служить ссылкой на кого-либо еще.

lxml.html.tostring() действительно не печатает предоставленный HTML, несмотря на pretty_print=True.

Однако у "брата" lxml.html - lxml.etree все работает хорошо.

Таким образом, можно использовать его следующим образом:

from lxml import etree, html

document_root = html.fromstring("<html><body><h1>hello world</h1></body></html>")
print(etree.tostring(document_root, encoding='unicode', pretty_print=True))

Вывод такой:

<html>
  <body>
    <h1>hello world</h1>
  </body>
</html>
person Jayesh Bhoot    schedule 12.05.2013
comment
Флаг pretty_print работает только при вызове etree.tostring с method='xml', что является значением по умолчанию. Итак, здесь мы имеем дело с XHTML. - person lenz; 08.07.2015
comment
Это отличный ответ, потому что он не использует никаких внешних зависимостей. Однако, если строка, содержащая HTML, имеет возврат каретки, etree.tostring ничего не украшает и возвращает свой ввод без изменений, по крайней мере, на Python 2.7.10 ... как только вы знаете, заменить возврат каретки несложно, но вы потратите много времени, если вы этого не знаете. - person Tom Swirly; 03.03.2016
comment
Это здорово, потому что предоставляет решение только для вкладок. Это не изменяет HTML другими способами, такими как решения BeautifulSoup. - person earthmeLon; 11.05.2018
comment
НЕА! И вот почему. etree.tostring сократит ‹i›‹/i› до ‹i/›, что недопустимо. - person shrewmouse; 11.01.2019

Если вы храните HTML как неформатированную строку в переменной html_string, это можно сделать с помощью BeautifulSoup4 следующим образом:

from bs4 import BeautifulSoup
print(BeautifulSoup(html_string, 'html.parser').prettify())
person AlexG    schedule 13.11.2017

Если добавить еще одну зависимость не проблема, вы можете использовать пакет html5print. Преимущество по сравнению с другими решениями заключается в том, что оно также украшает код CSS и Javascript, встроенный в документ HTML.

Чтобы установить его, выполните:

pip install html5print

Затем вы можете использовать его как команду:

html5-print ugly.html -o pretty.html

или как код Python:

from html5print import HTMLBeautifier
html = '<title>Page Title</title><p>Some text here</p>'
print(HTMLBeautifier.beautify(html, 4))
person pgmank    schedule 19.03.2018
comment
это устанавливает несколько других зависимостей, включая BeautifulSoup4 - person byteface; 26.09.2019

Я пробовал решения BeautifulSoup prettify и HTMLBeautifier html5print, но, поскольку я использую yattag для создания HTML, мне кажется более подходящим используйте его функцию indent, которая производит вывод с красивым отступом.

from yattag import indent

rawhtml = "String with some HTML code..."

result = indent(
    rawhtml,
    indentation = '    ',
    newline = '\r\n',
    indent_text = True
)

print(result)
person Vadym Pasko    schedule 06.05.2018

Под капотом lxml использует libxml2 для сериализации дерева обратно в строку. Вот соответствующий фрагмент кода, который определяет, следует ли добавлять новую строку после закрытия тега:

    xmlOutputBufferWriteString(buf, ">");
    if ((format) && (!info->isinline) && (cur->next != NULL)) {
        if ((cur->next->type != HTML_TEXT_NODE) &&
            (cur->next->type != HTML_ENTITY_REF_NODE) &&
            (cur->parent != NULL) &&
            (cur->parent->name != NULL) &&
            (cur->parent->name[0] != 'p')) /* p, pre, param */
            xmlOutputBufferWriteString(buf, "\n");
    }
    return;

Таким образом, если узел является элементом, не является встроенным тегом и за которым следует родственный узел (cur->next != NULL) и не является одним из p, pre, param, то он выводит новую строку.

person samplebias    schedule 27.05.2011

Не могли бы вы просто передать его в HTML Tidy? Либо из шелла, либо через os.system().

person tsm    schedule 27.05.2011
comment
Сначала я думал об использовании HTML Tidy, но мой код немного причудливый и аккуратный, но в конечном итоге это приводит к хаосу. Вместо этого решил использовать BeautifulSoup. Работал как шарм. - person bcosynot; 27.05.2011
comment
HTML Tidy исправляет ваш HTML, который может сломаться. Такие ошибки довольно трудно найти, если вы забываете, что HTML Tidy обрабатывает результаты (я знаю, о чем говорю)... - person mzuther; 23.01.2014
comment
Совсем недавно, чем в комментариях 2011 года, см. ответ на этот вопрос 2018 года: stackoverflow.com/questions/50380799/. Эта библиотека не работает и/или не работает с Python 3.5. Может сэкономит кому-то время... - person RBV; 18.05.2018

не совсем мой код, я его где-то подобрал

def indent(elem, level=0):
    i = '\n' + level * '  '
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = i + '  '
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
        for elem in elem:
            indent(elem, level+1)
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = i

Я использую его с:

indent(page)
tostring(page)
person sherpya    schedule 27.05.2011
comment
Да, это работает! - person Ideogram; 05.03.2021

Если вы не заботитесь о причудливой HTML-функциональности (например, вы должны полностью поддерживать те полчища клиентов, использующих Netscpae 2.0, поэтому наличие <br> вместо <br /> является обязательным), вы всегда можете изменить свой метод на "xml", который кажется Работа. Вероятно, это ошибка в lxml или в libxml, но я не смог найти ее причину.

person Boaz Yaniv    schedule 27.05.2011
comment
Когда вы устанавливаете метод в xml, если тег не имеет подэлементов, закрывающий тег не создается. Например, в рассматриваемом примере внутренний div не будет иметь закрывающего тега. Я действительно не знаю, почему. В итоге я использовал BeautifulSoup, чтобы получить правильный результат. - person bcosynot; 27.05.2011