Cómo imprimir bonito HTML a un archivo, con sangría

Estoy usando lxml.html para generar algo de HTML. Quiero imprimir bastante (con sangría) mi resultado final en un archivo html. ¿Cómo puedo hacer eso?

Esto es lo que he probado y conseguido hasta ahora (soy relativamente nuevo en Python y 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") 

Como puede ver, estoy usando el atributo pretty_print=True . Pensé que eso daría un código sangrado, pero en realidad no ayuda. Esta es la salida:

Terminé usando BeautifulSoup directamente. Eso es algo que lxml.html.soupparser usa para analizar HTML.

BeautifulSoup tiene un método de prettificación que hace exactamente lo que dice que hace. Pretende el HTML con guiones adecuados y todo.

BeautifulSoup NO corregirá el código HTML, por lo que el código roto permanece roto. Pero en este caso, dado que el código está siendo generado por lxml, el código HTML debe ser al menos semánticamente correcto.

En el ejemplo dado en mi pregunta, tendré que hacer esto:

 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 

Aunque mi respuesta podría no ser útil ahora, la estoy dejando caer aquí para que sirva de referencia para cualquier otra persona en el futuro.

lxml.html.tostring() , de hecho, no imprime el HTML proporcionado a pesar de que pretty_print=True .

Sin embargo, el “hermano” de lxml.htmllxml.etree tiene funcionando bien.

Así que uno podría usarlo como sigue:

 from lxml import etree, html document_root = html.fromstring("

hello world

") print(etree.tostring(document_root, encoding='unicode', pretty_print=True))

La salida es así:

   

hello world

Si almacena el HTML como una cadena sin formato, en una variable html_string , se puede hacer usando beautifulsoup4 de la siguiente manera:

 from bs4 import BeautifulSoup print(BeautifulSoup(html_string, 'html.parser').prettify()) 

Bajo el capó, lxml usa libxml2 para libxml2 a serializar el árbol en una cadena. Aquí está el fragmento de código relevante que determina si se debe agregar una nueva línea después de cerrar una etiqueta:

  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; 

Entonces, si un nodo es un elemento, no es una etiqueta en línea y es seguido por un nodo hermano ( cur->next != NULL ) y no es uno de p, pre, param entonces emitirá una nueva línea.

¿No podrías simplemente canalizarlo en HTML Tidy ? Ya sea desde el shell o a través de os.system() .

Si agregar una dependencia más no es un problema, puede usar el paquete html5print . La ventaja sobre las otras soluciones es que también embellece los códigos CSS y Javascript incrustados en el documento HTML.

Para instalarlo, ejecute:

 pip install html5print 

Luego, puedes usarlo como comando:

 html5-print ugly.html -o pretty.html 

o como código de Python:

 from html5print import HTMLBeautifier html = 'Page Title

Some text here

' print(HTMLBeautifier.beautify(html, 4))

Intenté las soluciones HTMLBeautifier BeautifulSoup y HTMLBeautifier de HTMLBeautifier , pero como estoy usando yattag para generar HTML, parece más apropiado usar su función de indent , que produce una salida con sangría agradable.

 from yattag import indent rawhtml = "String with some HTML code..." result = indent( rawhtml, indentation = ' ', newline = '\r\n', indent_text = True ) print(result) 

Si no te importa el HTML original (por ejemplo, debes ser totalmente compatible con las hordas de clientes que utilizan Netscpae 2.0, por lo que tener un código en lugar de
es obligatorio), siempre puedes cambiar tu método a “xml”. “, que parece funcionar. Este es probablemente un error en lxml o en libxml, pero no pude encontrar la razón para ello.

Realmente no es mi código, lo escogí en alguna parte

 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 

Lo uso con:

 indent(page) tostring(page)