Skip to main content

Command Palette

Search for a command to run...

You Scraped It. Now Serve It: Your First Flask Web Server

Published
9 min read

Last time, you pulled data from a webpage. Today, you flip to the other side — you'll serve webpages yourself.

Same HTML. Opposite perspective. By the end, you'll understand both sides of how the web works.

What Even Is Flask?

Before we write code, let's understand what we're building.

When you type a URL into your browser and hit enter, something has to answer. Something has to receive your request and send back a webpage. That something is a web server.

Flask is a Python library that turns your computer into a web server. It listens for incoming requests, figures out what you're asking for, and runs your Python code to generate a response.

Here's the mental model:

Without Flask: You write Python scripts that run once and exit.

With Flask: You write Python functions that wait for requests, run when someone visits a URL, and return whatever you want — HTML, images, files, anything.

The Request-Response Cycle

Every interaction on the web follows the same pattern:

  1. You make a request — typing bookstore.com/products in your browser

  2. The server receives it — Flask catches the request and looks at the path (/products)

  3. Flask matches a route — it finds the function you wrote to handle /products

  4. Your function runs — your Python code executes and returns data

  5. The response travels back — Flask sends your data to the browser

  6. Browser displays it — you see the webpage

Flask is essentially a traffic director. It routes incoming requests to your Python functions based on the URL path. That's the core idea. Everything else is just details.

What You'll Build

A working web server that:

  • Serves multiple HTML pages

  • Has working navigation between pages

  • Serves the same products page you scraped in the last tutorial

You'll go from "person who requests webpages" to "person who serves them."

Before We Start

What you need:

  • A Google account

  • 15 minutes

  • Ideally: completed the first scraping tutorial (helpful, not required)


Step 1: Set Up Your Workspace

Go to colab.research.google.com and create a new notebook.

Here's something important to understand: Colab runs on Google's servers, not your computer. When you start a Flask server in Colab, it's running somewhere in Google's data center. So how do you access it from your browser?

Colab can generate a special URL that connects your browser to your server. Think of it as Colab saying: "Here's a public address where people can reach the server you're about to build."

Run this cell to get your URL:

from google.colab.output import eval_js

url = eval_js("google.colab.kernel.proxyPort(5000)")
print(f"🌐 Your server URL: {url}")

You'll see a URL like https://xyz-abc-5000.googleusercontent.com.

Open this URL in a new browser tab now. You'll see an error page — that's expected! There's no server running yet. Keep this tab open throughout the tutorial.


Step 2: Your First Flask Server

Now let's create something for that URL to show. In a new cell:

from flask import Flask
import threading

# Create a Flask application
app = Flask(__name__)

# Define what happens when someone visits "/"
@app.route('/')
def home():
    return "Hello from Flask!"

# Start the server
threading.Thread(target=app.run, kwargs={'port': 5000}).start()

Run this cell, then open your URL in the browser. You should see: Hello from Flask!

Let's break down every line:

from flask import Flask — Import the Flask class from the flask library.

import threading — We need this because app.run() normally blocks (waits forever). Threading lets the server run in the background so Colab stays responsive.

app = Flask(__name__) — Create a new Flask application. The __name__ tells Flask where to find resources. For now, just know this is the standard way to create a Flask app.

@app.route('/') — This is a decorator. It tells Flask: "When someone visits the path /, run the function below." The / path is the root — like visiting google.com instead of google.com/search.

def home(): — Just a regular Python function. The name doesn't matter to Flask; only the decorator above it matters.

return "Hello from Flask!" — Whatever you return gets sent to the browser. Right now it's plain text. Soon we'll return HTML.

threading.Thread(...).start() — Start the Flask server on port 5000 in a background thread. This is Colab-specific; normally you'd just write app.run().


Step 3: Add More Routes

One route is boring. Let's add more pages.

Important: Before running new Flask code, restart the runtime (Runtime → Restart runtime), then re-run Step 1 to get your URL, then run the new code. This clears the old server.

from flask import Flask
import threading

app = Flask(__name__)

@app.route('/')
def home():
    return "Welcome to the bookstore!"

@app.route('/about')
def about():
    return "We sell Python books."

@app.route('/contact')
def contact():
    return "Email us at books@example.com"

threading.Thread(target=app.run, kwargs={'port': 5000}).start()

Now try these in your browser (just change the end of your URL):

  • your-url/ → "Welcome to the bookstore!"

  • your-url/about → "We sell Python books."

  • your-url/contact → "Email us at books@example.com"

See the pattern? Each @app.route() decorator creates a new page. The path in the decorator ('/', '/about', '/contact') maps to a function that returns content.

Think of it like a receptionist:

  • Visitor asks for "/" → receptionist calls home()

  • Visitor asks for "/about" → receptionist calls about()

  • Visitor asks for "/unknown" → receptionist says "404 Not Found"


Step 4: Return Real HTML

Plain text is boring. Let's return actual HTML that browsers can render with styles and links.

from flask import Flask
import threading

app = Flask(__name__)

@app.route('/')
def home():
    return """
    <!DOCTYPE html>
    <html>
    <head>
        <title>Python Bookstore</title>
        <style>
            body { font-family: Arial, sans-serif; padding: 40px; }
            h1 { color: #2c3e50; }
            a { color: #3498db; }
        </style>
    </head>
    <body>
        <h1>Welcome to the Python Bookstore</h1>
        <p>Your source for Python learning materials.</p>
        <p><a href="/products">Browse our products →</a></p>
    </body>
    </html>
    """

@app.route('/products')
def products():
    return "<h1>Products coming soon!</h1>"

threading.Thread(target=app.run, kwargs={'port': 5000}).start()

Restart your runtime, re-run Step 1 to get your URL, then run this code. Open your URL — now you see a styled page with a clickable link!

What changed: Instead of returning a plain string, we return a multi-line string containing HTML. Flask doesn't care what you return — it just sends it to the browser. The browser interprets it as HTML and renders it visually.

Click the "Browse our products" link. You navigate to /products. That's a real website with working navigation.


Step 5: Serve the Products Page

Remember the bookstore HTML from the scraping tutorial? Let's serve it from our Flask server.

from flask import Flask
import threading
import requests

app = Flask(__name__)

# Fetch the HTML once when the server starts
response = requests.get('https://umanggulati.github.io/python-demo/products.html')
products_html = response.text
print(f"✅ Loaded {len(products_html)} characters of HTML")

@app.route('/')
def home():
    return """
    <h1>Python Bookstore</h1>
    <p><a href="/products">View Products</a></p>
    """

@app.route('/products')
def products():
    return products_html  # Serve the HTML we fetched

threading.Thread(target=app.run, kwargs={'port': 5000}).start()

Let's walk through what this code does:

import requests — We need the requests library to fetch web pages (same one we used for scraping).

response = requests.get('https://...') — Before the server even starts, we fetch the HTML from our GitHub Pages site. This happens once when you run the cell.

products_html = response.text — Store the HTML content in a variable. This sits in memory, ready to serve.

@app.route('/products') — When someone visits /products...

return products_html — ...we send them the HTML we fetched earlier.

The key insight: the fetching happens once when the server starts. After that, every visitor to /products gets the same stored HTML instantly — we're not fetching it again for each request.

Visit /products in your browser. You're now serving the exact same page you scraped in the last tutorial — but from your server. The visitor has no idea you fetched it from somewhere else.


Step 6: Build a Complete Multi-Page Site

Let's put everything together into a proper website with multiple pages and navigation:

from flask import Flask
import threading
import requests

app = Flask(__name__)

# Fetch the products HTML once at startup
response = requests.get('https://umanggulati.github.io/python-demo/products.html')
products_html = response.text
print(f"✅ Loaded products page")

# Shared navigation HTML
nav = """
<nav style="background: #2c3e50; padding: 15px;">
    <a href="/" style="color: white; margin-right: 20px;">Home</a>
    <a href="/products" style="color: white; margin-right: 20px;">Products</a>
    <a href="/about" style="color: white;">About</a>
</nav>
"""

@app.route('/')
def home():
    return f"""
    <!DOCTYPE html>
    <html>
    <head><title>Python Bookstore</title></head>
    <body style="font-family: Arial, sans-serif; margin: 0;">
        {nav}
        <div style="padding: 40px;">
            <h1>Welcome to the Python Bookstore</h1>
            <p>Your one-stop shop for Python learning materials.</p>
            <p>We have {products_html.count('product-card')} books available!</p>
            <a href="/products" style="background: #3498db; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px;">
                Browse Products
            </a>
        </div>
    </body>
    </html>
    """

@app.route('/products')
def products():
    return products_html

@app.route('/about')
def about():
    return f"""
    <!DOCTYPE html>
    <html>
    <head><title>About Us</title></head>
    <body style="font-family: Arial, sans-serif; margin: 0;">
        {nav}
        <div style="padding: 40px;">
            <h1>About Us</h1>
            <p>We're passionate about Python education.</p>
            <p>This bookstore is powered by Flask — the same page you learned to scrape, now served by your own code.</p>
        </div>
    </body>
    </html>
    """

threading.Thread(target=app.run, kwargs={'port': 5000}).start()

This looks like a lot, but it's just combining everything we've learned. Let's break it into sections:

Section 1: Setup and data fetching

response = requests.get('https://umanggulati.github.io/python-demo/products.html')
products_html = response.text

Same as Step 5 — fetch the products page once when the server starts and store it.

Section 2: Shared navigation

nav = """
<nav style="background: #2c3e50; padding: 15px;">
    <a href="/" style="color: white; margin-right: 20px;">Home</a>
    <a href="/products" style="color: white; margin-right: 20px;">Products</a>
    <a href="/about" style="color: white;">About</a>
</nav>
"""

This is just a string containing HTML for a navigation bar. We store it in a variable because we want the same navigation on every page. Instead of copy-pasting it into each route, we write it once and reuse it.

Section 3: Home page with dynamic content

@app.route('/')
def home():
    return f"""
    ...
        {nav}
    ...
        <p>We have {products_html.count('product-card')} books available!</p>
    ...
    """

Notice the f before the triple quotes — this is an f-string, which lets us insert Python variables directly into the HTML:

  • {nav} gets replaced with our navigation bar HTML

  • {products_html.count('product-card')} counts how many times "product-card" appears in the HTML (which tells us how many books there are) and inserts that number

So the visitor sees "We have 5 books available!" — a dynamic number calculated from real data.

Section 4: Products page

@app.route('/products')
def products():
    return products_html

Simple — just return the HTML we fetched earlier.

Section 5: About page

@app.route('/about')
def about():
    return f"""
    ...
        {nav}
    ...
    """

Another page that uses the same {nav} variable. If you ever want to change the navigation, you change it in one place and every page updates.

The result: A complete website with three pages and consistent navigation. Click around — Home, Products, About — it all works.


What You Learned

You now understand both sides of the web:

ConceptWhat it means
Flask appFlask(__name__) creates a web server
Routes@app.route('/path') maps URLs to functions
Returning HTMLWhatever you return gets sent to the browser
Multi-page sitesMultiple routes = multiple pages

In the scraping tutorial, you were the client — requesting data from servers.

Now you're the server — responding to requests with pages.

Same web. Both sides. You get it now.


Resources:

More from this blog

Figure it Out

15 posts