Index Ai Academy
Phase 0 · ToolsTalking to APIs

Web pages: HTML and CSS for Python developers

About 40 minLooking ahead: some prerequisites aren't done yet

Why this matters

Python developers write HTML more often than they expect: email bodies, reports, the pages a web app sends, the templates in Flask. They read it even more: scraping, testing a web app, debugging what a server sent back. You don't need to be a designer. You need to know how a page is put together, what a form really sends, how selectors pick things out, and how to keep user text from breaking a page.

  • Read and write HTML: elements, attributes, links, lists and tables, as a tree
  • Explain what a form sends when it's submitted, with GET and with POST
  • Pick elements out with CSS selectors, the same selectors scraping and styling use
  • Escape text before putting it into a page, so names with < & and quotes can't break it

A page is a tree of elements

HTML is text with tags in angle brackets. An element is an opening tag, its content and a closing tag: <h1>Deals</h1>. Tags can carry attributes, like the address in a link: <a href="/shop/deals">Deals</a>. Elements sit inside other elements, so a page is a tree:

from preview import preview

page = """<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Trail Club news</title>
</head>
<body>
  <h1>Trail Club news</h1>
  <p>Saturday's hike starts at <strong>8:00</strong> at Fern Hollow.</p>
  <ul>
    <li><a href="/shop/deals?page=1">This week's deals</a></li>
    <li>New: the <em>Lookout</em> down vest</li>
  </ul>
  <img src="data:image/gif;base64,R0lGODlhAQABAAAAACw=" alt="A trail map">
</body>
</html>"""
preview(page)
print(len(page), "characters of HTML")

preview shows the page under the output, in a frame of its own. The <head> holds facts about the page, like its <title> for the browser tab. The <body> holds what you see. <ul> is a list and each <li> an item in it. <strong> and <em> mark important and stressed words. <img> has no closing tag, and its alt text describes the image for anyone who can't see it.

Tables, and the attributes that matter

Tables hold rows (<tr>) of cells: <th> for headings, <td> for data. Two attributes do a lot of work on any element:

  • class names a group of elements, like class="price" on every price. An element can have several classes, separated by spaces.
  • id names one element, unique on the page.
from preview import preview

rows = [("Ridgeline 1-person tent", 200), ("Ember spork", 5), ("Scree hiking boot", 190)]
cells = "\n".join(f'  <tr><td>{name}</td><td class="price">${price:.2f}</td></tr>' for name, price in rows)
table = f"""<table id="catalog">
  <tr><th>Product</th><th>Price</th></tr>
{cells}
</table>
<style>
  table {{ border-collapse: collapse; font-family: sans-serif; }}
  td, th {{ border: 1px solid #ccc; padding: 4px 10px; }}
  .price {{ text-align: right; color: #1a6b3c; }}
</style>"""
preview(table)
print(table.count("<tr>"), "rows")

The <style> element holds CSS, the language for how things look. Each rule is a selector, which says which elements it applies to, and then properties in braces. td, th means every data cell and heading cell. .price means every element with the class "price". (Inside the f-string, CSS's braces are doubled, {{ }}, so Python doesn't read them as gaps to fill.)