SEO

What is SEO?

SEO is the practice of improving a website so that it ranks higher in search engine results pages such as on Google. The goal here is to increase organic (non-paid) traffic to your website.

Structured Data

Structured data is a standardised format for providing information about a web page and classifying its content. It helps with SEO by making search engines like Google understand your content more clearly, leading to enhanced search results, such as rich snippets, which are the individual sections of structured data that show in search engine results beyond your typical title, URL, and description.

For structured data, see schema.org. It usually takes the form of JSON-LD which is preferred by Google. This is wrapped in a:

<script type="application/ld+json"></script>

element inside, and at the end of, your head element. It lists the individual rich snippets as a dictionary. An example for a hotel website would be:

<script type="application/ld+json">
    {
        "@context": "https://schema.org",
        "@type": "Hotel",
        "name": "Hotel Name",
        "image": "https://image_url.png",
        "description": "Website Description.",
        "url": "https://www.example_website.com/",
        "telephone": "0123456789",
        "email": "example_email@gmail.com",
        "address": {
            "@type": "PostalAddress",
            "streetAddress": "Road name",
            "addressLocality": "Area Name",
            "addressCountry": "Country Code"
        },
        "amenityFeature": [
            {
                "@type": "LocationFeatureSpecification",
                "name": "Feature",
                "value": true
            },
        ],
        "checkinTime": "14:00",
        "checkoutTime": "12:00",
        "starRating": {
            "@type": "Rating",
            "ratingValue": "4",
            "bestRating": "5"
        },
        "priceRange": "$$",
        "geo": {
            "@type": "GeoCoordinates",
            "latitude": 1,
            "longitude":1
        }
    }
</script>

Microdata

Alternatively you can assign microdata within your HTML files. Wrap your content with a div with itemscope and itemtype. Then define an itemprop for each element inside to give more details. The values that the itemprop can take depend on the itemtype. For example you can find the hotel ones here.

Example from the schema website:

<div itemscope itemtype="https://schema.org/Hotel">
    <h1><span itemprop="name">ACME Hotel Innsbruck</span></h1>
    <span itemprop="description">
        A beautifully located business hotel right in the heart of the alps. Watch the sun rise over the scenic Inn 
        valley while enjoying your morning coffee.
    </span>
    <div itemprop="address" itemscope itemtype="https://schema.org/PostalAddress">
        <span itemprop="streetAddress">Technikerstrasse 21</span>
        <span itemprop="postalCode">6020</span>
        <span itemprop="addressLocality">Innsbruck</span>
        <span itemprop="addressRegion">Tyrol</span>,
        <span itemprop="addressCountry">Austria</span>
    </div>
    Phone: <span itemprop="telephone">+43 512 8000-0</span>
    <img itemprop="photo" src="../media/hotel_front.png" alt="Front view of the hotel" />
    Star rating: <span itemprop="starRating" itemscope itemtype="https://schema.org/Rating">
        <meta itemprop="ratingValue" content="4">****</span>
    Room rates: <span itemprop="priceRange">$100 - $240</span>
</div>

SEO Example

For this website (built with Django) I created a structured_data.html file in my root directory templates folder and included it in my base.html template underneath an extra meta block with {% include "includes/structured_data.html" %}

structured_data.html took the form of a <script> element with type="application/ld+json" and then using the Django templating language I can add in variables for each page.

structured_data.html

<script type="application/ld+json">
    {
        "@context": "https://schema.org",
        "@type": "{{ schema_type }}",
        "name": "{{ page_title }}",
        "articleSection": "{{ section }}",
        "headline": "{{ page_title }}",
        "description": "{{ page_description }}",
        "keywords":  "{{ keywords }}",
        "about": {
            "@type": "Thing",
            "name": "{{ about_name }}"
        },
        "isPartOf": {
            "@type": "CreativeWorkSeries",
            "name": "{{ isPartOf_name }}"
        },
        "author": {
            "@type": "Person",
            "name": "My name",
            "sameAs": [
                "github.com/mygithub"
            ]
        },
        "publisher": {
            "@type": "Person",
            "name": "My name"
        },
        "mainEntityOfPage": {
            "@type": "WebPage",
            "@id": "{{ request.build_absolute_uri }}"
        },
        "inLanguage": "en-GB",
        "url": "{{ request.build_absolute_uri }}"
    }
</script>

These variables were defined in an seo.py file in each app (sitting on the same level next to the urls.py, views.py etc files). In this file I created a library (e.g. HTML_SEO for the HTML app) that contained dictionaries for each page.

seo.py

HTML_SEO = {
    "home": {
        "page_title": "Learn HTML: Elements, Attributes, Nesting, IDs, Classes and DOCTYPE",
        "page_description": "Learn HTML fundamentals including elements, nesting, attributes, IDs, classes, comments and the HTML5 DOCTYPE with practical examples for building webpages.",
        "schema_type": "TechArticle",
        "slug": "home",
        "section": "HTML",
        "keywords": [
            "HTML",
            "HTML tutorial",
            "HTML elements",
            "HTML attributes",
            "HTML nesting",
            "HTML tags",
            "HTML5 DOCTYPE",
            "HTML IDs and classes",
            "web development"
        ],
        "about_name": "HTML Introduction",
        "isPartOf_name": "HTML Tutorial",
    },
}

Then in my views.py I imported this and set up the context for each page to include these variables. The keywords were defined as a list in the seo.py file so in the view they are joined to fit the required JSON syntax so that they appear as one concatenated string rather than a list of strings.

views.py

from django.shortcuts import render
from django.conf import settings
from .seo import HTML_SEO

def HTML_home(request):
    '''Return the HTML homepage'''
    context = HTML_SEO["home"].copy()
    context["keywords"] = ", ".join(context["keywords"])
    return render(
        request,
        'HTML/home/home.html',
        context
    )

For each page, the populated <script> element was taken from inspecting in Google Dev Tools and put through Google's rich results test to ensure that they were valid and would work as intended.