69

I am looking for a way to create html files dynamically in python. I am writing a gallery script, which iterates over directories, collecting file meta data. I intended to then use this data to automatically create a picture gallery, based on html. Something very simple, just a table of pictures.

I really don't think writing to a file manually is the best method, and the code may be very long. So is there a better way to do this, possibly html specific?

8 Answers 8

95

Dominate is a Python library for creating HTML documents and fragments directly in code without using templating. You could create a simple image gallery with something like this:

import glob
from dominate import document
from dominate.tags import *

photos = glob.glob('photos/*.jpg')

with document(title='Photos') as doc:
    h1('Photos')
    for path in photos:
        div(img(src=path), _class='photo')


with open('gallery.html', 'w') as f:
    f.write(doc.render())

Output:

<!DOCTYPE html>
<html>
  <head>
    <title>Photos</title>
  </head>
  <body>
    <h1>Photos</h1>
    <div class="photo">
      <img src="photos/IMG_5115.jpg">
    </div>
    <div class="photo">
      <img src="photos/IMG_5117.jpg">
    </div>
  </body>
</html>

Disclaimer: I am the author of dominate

Sign up to request clarification or add additional context in comments.

3 Comments

Best thing since sliced bread. I have dabbled with the same ideas for 20 years, with false starts in C, Lisp and IIRC (shudder) PHP but my half-baked solutions were always even more painful than just sprinkling HTML between code (PHP) or print-ing HTML. The Dominate library just clicks for me. To me Dominate feels very lisp-y in the sense that you always hear that Lisp is great for using the language itself as a Domain Specific Language. Well, it turns out Python is really good for this as well!
Agreed! This is truly awsome
Well done, @Knio. Your library is still holding up after more than a decade. This is exactly what I needed.
18

I think, if i understand you correctly, you can see here, "Templating in Python".

Comments

15

Use a templating engine such as Genshi or Jinja2.

Comments

12

Python is a batteries included language. So why not use xml.dom.minidom?

from typing import List
from xml.dom.minidom import getDOMImplementation, Document


def getDOM() -> Document:
    impl = getDOMImplementation()
    dt = impl.createDocumentType(
        "html",
        "-//W3C//DTD XHTML 1.0 Strict//EN",
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd",
    )
    return impl.createDocument("http://www.w3.org/1999/xhtml", "html", dt)


def ul(items: List[str]) -> str:
    dom = getDOM()
    html = dom.documentElement
    ul = dom.createElement("ul")
    for item in items:
        li = dom.createElement("li")
        li.appendChild(dom.createTextNode(item))
        ul.appendChild(li)
    html.appendChild(ul)
    return dom.toxml()


if __name__ == "__main__":
    print(ul(["first item", "second item", "third item"]))

outputs:

<?xml version="1.0" ?>
<!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>
<html>
    <ul>
        <li>first item</li>
        <li>second item</li>
        <li>third item</li>
    </ul>
</html>

The interface does not look like pythonic, but if you have been a fronted developer and used JavaScript DOM manipulation, it matches your mind better and yes it frees you from adding a needless dependency.

Comments

2

Templating, as suggested in other answers, is probably the best answer (I wrote an early, quirky templating module called yaptu, but modern mature ones as suggested in other answers will probably make you happier;-).

However, though it's been a long time since I last used it, I fondly recall the Quixote approach, which is roughly a "reverse templating" (embedding HTML generation within Python, rather than viceversa as normal templating does). Maybe you should take a look and see if you like it better;-).

1 Comment

I clicked around a little and it seems Quixote is still around. A description of for instance the button generator is here: quixote.ca/doc/widgets.txt . All in all, it seems like you can put the Dominate library and Quixote in the same general category. But Quixote seems to do a lot of other things too, server related things. I like your term "reverse templating" - I will start using it to describe the general technique!
2

Just write a file that contains html

a = '''
<html>
<head>
<h1> hello </h1>
</head>
</html>
  
'''
f = open("myhtmlfile.html", "a")
f.write(a)

Comments

0

An alternative to Dominate is Chope, which also supports CSS.

Here's an example:

from chope import *
from chope.css import *

page = html[
    head[
        style[
            Css[
                'body': dict(
                    background_color='linen',
                    font_size=pt/12
                ),
                '.inner-div': dict(
                    color='maroon',
                    margin_left=px/40
                )
            ]
        ]
    ],
    body[
        h1['Title'],
        div(class_='outer-div')[
            div(class_='inner-div')[
                'Some content.'
            ]
        ]
    ]
]

Which will give you

<html>
  <head>
    <style>
      body {
        background-color: linen;
        font-size: 12pt;
      }
      
      .inner-div {
        color: maroon;
        margin-left: 40px;
      }
    </style>
  </head>
  <body>
    <h1>
      Title
    </h1>
    <div class="outer-div">
      <div class="inner-div">
        Some content.
      </div>
    </div>
  </body>
</html>

Disclaimer: I am the creator of Chope

Comments

0

Using ElementTree

You can also use the standard library:

from xml.etree import ElementTree as ET
resHTML = ET.Element('html',attrib={'lang':'en'})
body = ET.SubElement(resHTML,'body')
p1 = ET.SubElement(body,'p')
span1 = ET.SubElement(p1,'span', attrib={'id': 'anIDA'})
span1.text = "mytext"

with open("HTMLtest.html","wb") as o:
    o.write(b"<!DOCTYPE html>\n")
    o.write(ET.tostring(resHTML))

# cleanest  way using Beautiful Soup
import bs4
with open("HTMLtest.html","w") as o:
    o.write(bs4.BeautifulSoup(ET.tostring(resHTML), "html").prettify())

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.