Python: Cómo imprimir html en un archivo


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

Esto es lo que he intentado 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 puedes ver estoy usando el atributo pretty_print=True. Pensé que eso daría código indentado, pero en realidad no ayuda. Esta es la salida :

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

Author: bcosynot, 2011-05-27

9 answers

Terminé usando BeautifulSoup directamente. Eso es algo lxml.HTML.soupparser utiliza para analizar HTML.

BeautifulSoup tiene un método prettify que hace exactamente lo que dice que hace. Embellece el HTML con sangrías adecuadas y todo.

BeautifulSoup NO arreglará el 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
 70
Author: bcosynot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-06 03:56:57

Aunque mi respuesta podría no ser útil ahora, la estoy dejando aquí para actuar como una referencia a cualquier otra persona en el futuro.

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

Sin embargo, el "hermano" de lxml.html - lxml.etree lo tiene funcionando bien.

Así que uno podría usarlo de la siguiente manera:

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))

La salida es así:

<html>
  <body>
    <h1>hello world</h1>
  </body>
</html>
 28
Author: Jayesh Bhoot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-12 09:01:39

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())
 4
Author: AlexG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-13 07:26:52

Bajo el capó, lxml usa libxml2 para serializar el árbol de nuevo 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 de un nodo hermano (cur->next != NULL) y no es uno de p, pre, param entonces generará una nueva línea.

 2
Author: samplebias,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-27 15:40:45

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

 1
Author: tsm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-27 13:14:13

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 el código CSS y Javascript incrustado en el documento HTML.

Para instalarlo, ejecute:

pip install html5print

Entonces, puede usarlo como un comando:

html5-print ugly.html -o pretty.html

O como código Python:

from html5print import HTMLBeautifier
html = '<title>Page Title</title><p>Some text here</p>'
print(HTMLBeautifier.beautify(html, 4))
 1
Author: pgmank,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-19 20:49:27

Si no le importa la peculiaridad de HTMLness (por ejemplo, debe soportar absolutamente esas hordas de clientes que usan Netscpae 2.0, por lo que tener <br> en lugar de <br /> es una necesidad), siempre puede cambiar su método a "xml", que parece funcionar. Esto es probablemente un error en lxml o en libxml, pero no pude encontrar la razón para ello.

 0
Author: Boaz Yaniv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-27 12:56:17

No es realmente mi código, lo elegí en algún lugar

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)
 0
Author: sherpya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-27 15:22:27

Probé las soluciones prettify de BeautifulSoup y HTMLBeautifierde html5print, pero como estoy usando yattag para generar HTML, parece más apropiado usar su función indent, que produce una salida bien sangrada.

from yattag import indent

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

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

print(result)
 0
Author: Vadym Pasko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-06 08:02:01