J'utilise lxml.html pour générer du HTML. Je veux imprimer assez (avec indentation) mon résultat final dans un fichier html. Comment je fais ça?
C'est ce que j'ai essayé et obtenu jusqu'à présent (je suis relativement nouveau pour Python et 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")
Comme vous pouvez le voir, j'utilise le pretty_print=True
attribut. Je pensais que cela donnerait du code en retrait, mais cela n'aide pas vraiment. Voici la sortie:
<div style="overflow-x: hidden; overflow-y: hidden;" class="scroll"><div style="width: 4340px;" class="scrollContainer"></div></div>
J'ai fini par utiliser BeautifulSoup directement. C'est quelque chose que lxml.html.soupparser utilise pour analyser HTML.
BeautifulSoup a une méthode prettify qui fait exactement ce qu'elle dit. Il embellit le HTML avec des retraits appropriés et tout.
BeautifulSoup ne corrigera PAS le code HTML, donc le code cassé reste cassé. Mais dans ce cas, puisque le code est généré par lxml, le code HTML doit être au moins sémantiquement correct.
Dans l'exemple donné dans ma question, je devrai faire ceci:
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
Bien que ma réponse ne soit peut-être pas utile maintenant, je la laisse tomber ici pour servir de référence à quelqu'un d'autre à l'avenir.
lxml.html.tostring()
, en effet, n'imprime pas assez le HTML fourni malgré pretty_print=True
.
Cependant, le "frère" de lxml.html
- lxml.etree
fonctionne bien.
On pourrait donc l'utiliser comme suit:
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 sortie est comme ceci:
<html>
<body>
<h1>hello world</h1>
</body>
</html>
Si vous stockez le code HTML sous forme de chaîne non formatée, dans une variable html_string
, cela peut être fait en utilisant beautifulsoup4 comme suit:
from bs4 import BeautifulSoup
print(BeautifulSoup(html_string, 'html.parser').prettify())
Sous le capot, lxml
utilise libxml2
pour sérialiser l'arborescence dans une chaîne. Voici l'extrait de code pertinent qui détermine s'il faut ajouter une nouvelle ligne après la fermeture d'une balise:
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;
Donc, si un nœud est un élément, n'est pas une balise en ligne et est suivi d'un nœud frère (cur->next != NULL
) et ne fait pas partie de p, pre, param
alors il affichera une nouvelle ligne.
Si l'ajout d'une dépendance supplémentaire n'est pas un problème, vous pouvez utiliser le package html5print . L'avantage par rapport aux autres solutions, c'est qu'il embellit également le code CSS et Javascript incorporé dans le document HTML.
Pour l'installer, exécutez:
pip install html5print
Ensuite, vous pouvez soit l'utiliser comme une commande:
html5-print ugly.html -o pretty.html
ou comme Python code:
from html5print import HTMLBeautifier
html = '<title>Page Title</title><p>Some text here</p>'
print(HTMLBeautifier.beautify(html, 4))
J'ai essayé les solutions prettify
de BeautifulSoup et HTMLBeautifier
de html5print mais comme j'utilise yattag pour générer du HTML, il semble plus approprié d'utiliser sa fonction indent
, qui produit une sortie bien en retrait.
from yattag import indent
rawhtml = "String with some HTML code..."
result = indent(
rawhtml,
indentation = ' ',
newline = '\r\n',
indent_text = True
)
print(result)
Ne pourriez-vous pas simplement le diriger dans HTML Tidy ? Soit depuis le Shell, soit via os.system()
.
Si vous ne vous souciez pas du HTMLness original (par exemple, vous devez absolument prendre en charge ces hordes de clients utilisant Netscpae 2.0, donc avoir <br>
au lieu de <br />
est un must), vous pouvez toujours changer votre méthode en "xml", ce qui semble fonctionner. C'est probablement un bogue dans lxml ou dans libxml, mais je n'ai pas pu en trouver la raison.
pas vraiment mon code, je l'ai choisi quelque part
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
Je l'utilise avec:
indent(page)
tostring(page)