Creating a Flask app that serves HTML

A Flask web application can serve up HTML webpages just as easy as "Hello world", because HTML is just text.
This article is part of a sequence:
Introduction to Simple Web Applications with Flask
A series on learning how to make simple, one-file Flask apps.

Summary

Because HTML is just plain text, formatted according to a specification (which, incidentally, is the same concept behind Python and every other programming language), creating a Flask app that serves up HTML is just a matter of knowing how to make a Flask app, period, and then programming it to return a message in HTML, i.e. you have to learn a little HTML.

However, this is not a web design/development course, and so it is not worth our time to do a comprehensive review of HTML syntax. But we do need to understand enough to know the concept and to recognize HTML when we see it. If you already know HTML, you can skim this section.

If you really want to know more about HTML, one of the best introductions to HTML (and CSS and JS) can be found in Chapter 3 of Scott Murray’s Interactive Data Visualization, which can be read online for free.

Objectives

  • Learn the difference between how a browser interprets a string of text and a string of text that happens to be formatted as HTML.
  • How to make a hyperlink in HTML.
  • How to make a HTML page without a web app.
Table of contents

(Re)create a basic Flask app

For this lesson, we'll start off with a boilerplate, one-file Flask app, i.e. just app.py. We won't worry yet about making multiple pages or multiple file.

See if you can create the boilerplate from memory:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def homepage():
    return """Hello world!"""

if __name__ == '__main__':
    app.run(use_reloader=True, debug=True)

And switch to the command-line and get it running:

python app.py

Then visit 127.0.0.1:5000 (i.e. localhost:5000)

The limitations of text and the purpose of HTML

Program your app.py to return a multi-line text string message for the / route, e.g.

@app.route('/')
def homepage():
    return """
    Hello world!
    
    This is my first web app!

    I'm so excited!
    """

No matter how many newlines of text are in your message, your web browser will render it as one single line:

image multiline-text.png

By default, the Flask app responds to the web browser with a heads-up that it is sending along data that should be interpreted as "text/html".

Inspecting a web server's response

A quick segue that will be more relevant when we learn web-scraping: let's see the metadata behind our localhost web server's response. More specifically, let's confirm that the Flask app is sending its response with the indication that it is mean to be interpreted as HTML

If you know how to use your browser's developer tools, you can view the headers your response:

image inspector-localhost-response-type.png

Or heck, get some more Python practice in. Open up a new Terminal window/tab, jump into ipython and perform a HTTP request against your local web server. Then examine the response object's headers:

import requests
resp = requests.get("http://localhost:5000")
resp.text
# => "\n    Hello world!\n\n    This is my first web app!\n\n    I'm so excited!\n    "
resp.url
# => 'http://localhost:5000/'
resp.headers
# => {'Content-Type': 'text/html; charset=utf-8', 'Date': 'Tue, 05 Apr 2016 21:49:45 GMT', 'Content-Length': '74', 'Server': 'Werkzeug/0.11.3 Python/3.5.1'}

HTML and whitespace insensitivity

If you haven't figured it out by now, the Python language is whitespace sensitive; or, in other words, white space is significant.

Or, in more specific words: Python cares about exactly how many consecutive space characters are at the beginning of each line of code.

This works:

for i in range(5):
    print(   "Hello world"  )
    print("for the", i, "time.")

And this throws an error:

for i in range(5):
    print(   "Hello world"  )
   print("for the", i, "time.")

HTML, on the other hand, does not care. So when a browser sees _one or more whitespace characters__ – and whitespace characters include spaces and new lines – within a string of text, it will render those consecutive whitespaces as a single whitespace. Well, as long as those whitespace characters occur between non-whitespace characters.

This means that the following browser output:

Hello world ! ?

– can be represented by any of the following HTML strings:

 Hello     world !  ?
  Hello
world
!
?
Hello                                            world 
                  ! 


                  ?

What is HTML

So if HTML is just plain text, then we can write our web app's responses as plain text strings. But what makes HTML more than just plain text is its syntax and specification, particularly, its concept of text elements enclosed in tags.

To have a browser render 2 separate lines of text, we don't rely on newline whitespace characters. We enclose the each line of text, separately, within HTML tags. Traditionally, the paragraph tag – represented by <p> – is used to denote paragraphs of body text:

<p>Hello</p>
<p>world</p>

Add this as a string to app.py; I include the entirety of app.py, so far, just in case you're lost:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def homepage():
    return """
    <p>Hello</p>
    <p>world</p>
    """

if __name__ == '__main__':
    app.run(debug=True, use_reloader=True)

Reload localhost:5000 in your browser to see the result:

image p-hello-world.png

For reasons that are beyond the scope of this lesson, it is not necessary to memorize that <p> stands for "paragraph", or that it is the only way to denote blocks of text. Or that all browsers/sites render <p> the same way.

It is important to understand the nature of HTML and its syntax, such as how the angled brackets denote tags that are interpreted by the browser, but are not rendered by the browser…i.e. the <p> and </p> parts of our web app response do not show up to the web browser user. They are code that is meant only for the browser.

This concept of what you see is not what you get is fundamental to understanding HTML, and well, computational languages in general. It is definitely a core concept in understanding how web scraping works…

We won't be learning 1% of all the menial details of HTML and its syntax. But it is important to understand one common feature: making a hyperlink. You do want to memorize that the tags for hyperlinks are denoted with <a>. To designate that a string of text is intended to be a link to be clicked – and also, the destination of the hyperlink, we use the following syntax:

Hello <a href="https://www.planet.com/">world</a>!!!

Alter your app.py:

@app.route("/")
def homepage():
    return """
    Hello
    <a href="https://www.planet.com">world</a>
    """

And the result:

image a-hello-world.png

Don't worry about the style of the hyperlink – changing what it looks like is beyond the scope of our HTML lesson. But note how only the word world is a hyperlink. And note how, in the HTML, the href attribute denotes the destination, which has nothing to do with the content of the visible text itself. That is, the web page would look exactly the same if we did this:

(note, again, how whitespace is insignificant in what the browser renders)

@app.route("/")
def homepage():
    return """
    Hello
    <a href="http://www.milliondollarhomepage.com">
      world
    </a>
    """

A simple, styled webpage

The fact that a modern web browser will actually show something for a web server response as simple as:

  <p>Hello World</p>

– or even, just:

  Hello World

Is a reflection of the fact that modern web browsers have been engineered to just "roll with it" when it comes to with malformed HTML that doesn't follow the actual official HTML specification…this lenient attitude is obviously not the way the Python interpreter has been designed…but it makes for a nicer user experience when visiting a webpage that is slightly off.

When making an actual web app, though, it's worth following as much of the HTML spec as possible just so that when your served-up webpages don't act like you think they should, it won't be because you had overestimated how much the web browser would cover up for your sloppiness.

So what is the most minimalist HTML document that meets the HTML5 spec? According to the answer to this Stack Overflow question:

<!DOCTYPE html>
<title></title>

While that's a valid webpage, it will display exactly nothing in a web browser. So here's a "fatter" webpage that contains the typical tags of most websites in production today:

<!DOCTYPE html>
<head>
   <title>My title</title>
</head>
<body>  
    <h1>Visible stuff goes here</h1>
    <p>here's a paragraph, fwiw</p>
</body>

Adding an image

Of course, webpages generally have more than just text. So let's use the image tagto include the image at this URL:

http://stash.compjour.org/assets/images/sunset.jpg

As I've said at the beginning of these set of simple Flask lessons, we're trying to keep our initial web apps contained to a single file, for simplicity's sake. So for now, we "hotlink" a remote image (retrieve it from a remote server):

<!DOCTYPE html>
<head>
   <title>My title</title>
</head>
<body>  
    <h1>Visible stuff goes here</h1>
    <p>here's a paragraph, fwiw</p>
    <p>And here's an image:</p>
    <img src="http://stash.compjour.org/assets/images/sunset.jpg" alt="it's a nice sunset">
</body>

In general, it's better to host our own images rather than use another web server's bandwidth. But let's at least give the original author credit for the image by including the source URL:

https://www.flickr.com/photos/zokuga/14615349406/

And let's make the image itself a clickable link by wrapping the <img> element with an <a> element:

<!DOCTYPE html>
<head>
   <title>My title</title>
</head>
<body>  
    <h1>Visible stuff goes here</h1>
    <p>here's a paragraph, fwiw</p>
    <p>And here's an image:</p>
    <a href="https://www.flickr.com/photos/zokuga/14615349406/">
        <img src="http://stash.compjour.org/assets/images/sunset.jpg" alt="it's a nice sunset">
    </a>
</body>

Adding an external CSS style sheet

Even with a pretty image, our webpage looks a bit dated with the default typography styles and spacing:

image unstyled-html-page.jpg

CSS – the language used to define the visual style of a website – is yet another thing we'll skim over. But we can get its benefits by including the CSS from a stylesheet someone else has already created.

Let's include the stylesheet from the popular Foundation framework by using the <link> tag within the <head> element. Here's the URL for that particular stylesheet, which you can click through to see the raw code if you're curious:

http://stash.compjour.org/assets/css/foundation.css

(again, note that we're hotlinking to a remote file)

<!DOCTYPE html>
<head>
   <title>My title</title>
   <link rel="stylesheet" href="http://stash.compjour.org/assets/css/foundation.css">
</head>
<body>  
    <h1>Visible stuff goes here</h1>
    <p>here's a paragraph, fwiw</p>
    <p>And here's an image:</p>
    <a href="https://www.flickr.com/photos/zokuga/14615349406/">
        <img src="http://stash.compjour.org/assets/images/sunset.jpg" alt="it's a nice sunset">
    </a>
</body>

(Read more about CSS and stylesheets at the W3C)

A little inline CSS

If you view the webpage now generated by the app.py, you'll see that it's a little too flush against the edge of the browser:

image foundation-flush-page.jpg

We can change that by adding some CSS to the style attribute of the entire <body> element, like so:

<body style="width: 880px; margin: auto;">  

Note that this is most definitely not best practice. I'm just doing it here as a quick hack because we're almost done with this lesson. And because I'll be using it again in subsequent examples to keep the entire web app contained in one file. Best practice is to include CSS code in a self-contained stylesheet that we link to, as we did with the Foundation CSS.

The result of this styling:

image foundation-automargin.jpg

Much nicer!

All together

Here is all the code needed for the app.py that runs our little pretty-HTML generating Flask app:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def homepage():
    return """
<!DOCTYPE html>
<head>
   <title>My title</title>
   <link rel="stylesheet" href="http://stash.compjour.org/assets/css/foundation.css">
</head>
<body style="width: 880px; margin: auto;">  
    <h1>Visible stuff goes here</h1>
    <p>here's a paragraph, fwiw</p>
    <p>And here's an image:</p>
    <a href="https://www.flickr.com/photos/zokuga/14615349406/">
        <img src="http://stash.compjour.org/assets/images/sunset.jpg" alt="it's a nice sunset">
    </a>
</body>
"""

if __name__ == '__main__':
    app.run(debug=True, use_reloader=True)

Conclusion

And this concludes the extent of the HTML knowledge that we need to cover in order to build a web application…for now. If you're really fascinated by the specifics of Hypertext Markup Language, one of the best introductions to HTML (and CSS and JS) can be found in Chapter 3 of Scott Murray's Interactive Data Visualization, which can be read online for free.

In subsequent lessons, we'll be creating more complex webpages with more HTML tags and syntax, but it's enough to just know that writing HTML has nothing to do with writing the Python code that runs the Flask app, other than changing the contents of a text string.

How to make a HTML page without a web application

If HTML is just text, then why can't we just type all that HTML in the above example and save it as a HTML file? In fact, you can do that. Go ahead and copy this:

<!DOCTYPE html>
<head>
   <title>My title</title>
   <link rel="stylesheet" href="http://stash.compjour.org/assets/css/foundation.css">
</head>
<body style="width: 880px; margin: auto;">  
    <h1>Visible stuff goes here</h1>
    <p>here's a paragraph, fwiw</p>
    <p>And here's an image:</p>
    <a href="https://www.flickr.com/photos/zokuga/14615349406/">
        <img src="http://stash.compjour.org/assets/images/sunset.jpg" alt="it's a nice sunset">
    </a>
</body>

And save it somewhere on your computer with a .html extension. Double-clicking it should result in your operating system in opening it with your web browser. And it will look exactly the same as the webpage generated by our Flask app…which should make sense because it's the exact same HTML.

So what's the whole point of this Flask app business and programming in Python? In the next lesson, we'll create our first dynamic web app functionality.

This article is part of a sequence:
Introduction to Simple Web Applications with Flask
A series on learning how to make simple, one-file Flask apps.