You Scraped It. Now Serve It: Your First Flask Web Server
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:
You make a request — typing
bookstore.com/productsin your browserThe server receives it — Flask catches the request and looks at the path (
/products)Flask matches a route — it finds the function you wrote to handle
/productsYour function runs — your Python code executes and returns data
The response travels back — Flask sends your data to the browser
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:
| Concept | What it means |
| Flask app | Flask(__name__) creates a web server |
| Routes | @app.route('/path') maps URLs to functions |
| Returning HTML | Whatever you return gets sent to the browser |
| Multi-page sites | Multiple 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: