DEV Community

doron
doron

Posted on

A 10-Minute Technical SEO Audit with Python and DevTools

Technical SEO audits can become complicated very quickly.

You can crawl thousands of URLs, export dozens of reports, connect Search Console data, analyze Core Web Vitals, inspect structured data, and end up with a spreadsheet containing hundreds of issues.

Sometimes that is necessary.

But for many small and medium-sized websites, I prefer to start with something much simpler: a short technical check that answers a few basic questions.

  • Can search engines access the page?
  • Does the page return the correct HTTP status?
  • Is the canonical URL correct?
  • Is the page accidentally set to noindex?
  • Does it have a meaningful title and meta description?
  • Is there a single, clear H1?
  • Are important images missing alternative text?
  • Are there obvious internal linking problems?

Most of these checks can be automated with a surprisingly small Python script.

Building a Simple SEO Checker in Python

We'll use two libraries:

pip install requests beautifulsoup4
Enter fullscreen mode Exit fullscreen mode

Then create a file called:

quick_seo_check.py
Enter fullscreen mode Exit fullscreen mode

Here is a basic version:

import sys
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse


HEADERS = {
    "User-Agent": "Mozilla/5.0 (compatible; QuickSEOCheck/1.0)"
}


def analyze_page(url):
    try:
        response = requests.get(
            url,
            headers=HEADERS,
            timeout=10,
            allow_redirects=True
        )
    except requests.RequestException as error:
        print(f"Request failed: {error}")
        return

    print(f"\nURL: {url}")
    print(f"Final URL: {response.url}")
    print(f"Status code: {response.status_code}")

    soup = BeautifulSoup(response.text, "html.parser")

    # Title
    title = soup.title.string.strip() if soup.title and soup.title.string else ""
    print(f"\nTitle: {title}")
    print(f"Title length: {len(title)}")

    # Meta description
    description = soup.find("meta", attrs={"name": "description"})
    description_content = (
        description.get("content", "").strip()
        if description else ""
    )

    print(f"\nMeta description: {description_content}")
    print(f"Description length: {len(description_content)}")

    # Robots
    robots = soup.find("meta", attrs={"name": "robots"})
    robots_content = robots.get("content", "") if robots else "Not specified"
    print(f"\nMeta robots: {robots_content}")

    # Canonical
    canonical = soup.find("link", attrs={"rel": "canonical"})
    canonical_url = canonical.get("href") if canonical else "Missing"
    print(f"Canonical: {canonical_url}")

    # H1
    h1_tags = soup.find_all("h1")
    print(f"\nH1 count: {len(h1_tags)}")

    for index, h1 in enumerate(h1_tags, start=1):
        print(f"H1 #{index}: {h1.get_text(' ', strip=True)}")

    # Images without alt text
    images = soup.find_all("img")

    missing_alt = [
        img.get("src")
        for img in images
        if not img.has_attr("alt") or not img.get("alt", "").strip()
    ]

    print(f"\nImages: {len(images)}")
    print(f"Images missing alt text: {len(missing_alt)}")

    for image in missing_alt[:10]:
        print(f" - {image}")


if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: python quick_seo_check.py https://example.com/page")
        sys.exit(1)

    analyze_page(sys.argv[1])
Enter fullscreen mode Exit fullscreen mode

Run it like this:

python quick_seo_check.py https://example.com/
Enter fullscreen mode Exit fullscreen mode

In a few seconds you get a compact overview of some of the most common technical SEO problems on the page.

Why Start Small?

SEO tools are excellent, but sometimes they hide simple problems inside large reports.

For example, imagine a local business website with only 30 pages.

You probably don't need a 5,000-row spreadsheet to discover that:

/services/
Enter fullscreen mode Exit fullscreen mode

returns a 302 redirect,

/contact/
Enter fullscreen mode Exit fullscreen mode

has a noindex tag,

and three important landing pages share the same title.

A small script can expose those problems immediately.

This approach is especially useful for local business websites, where technical problems often affect a relatively small number of commercially important pages. That's something I regularly look at when working as an SEO consultant in Netanya and on similar local SEO projects.

The goal isn't to replace a crawler.

The goal is to find the obvious problems before opening a much larger toolbox.

Finding Internal Links

We can extend the script to collect all internal links found on the page.

Add this function:

def get_internal_links(soup, current_url):
    domain = urlparse(current_url).netloc
    links = set()

    for anchor in soup.find_all("a", href=True):
        href = anchor["href"].strip()

        if href.startswith(("#", "mailto:", "tel:", "javascript:")):
            continue

        absolute_url = urljoin(current_url, href)
        parsed_url = urlparse(absolute_url)

        if parsed_url.netloc == domain:
            clean_url = absolute_url.split("#")[0]
            links.add(clean_url)

    return sorted(links)
Enter fullscreen mode Exit fullscreen mode

Then inside analyze_page():

internal_links = get_internal_links(soup, response.url)

print(f"\nInternal links found: {len(internal_links)}")

for link in internal_links:
    print(f" - {link}")
Enter fullscreen mode Exit fullscreen mode

Now the script can quickly show where the page is sending users and crawlers.

That becomes useful when troubleshooting orphan pages, navigation problems, or landing pages that don't receive enough internal links.

Checking Links for Broken URLs

We can go one step further:

def check_link(url):
    try:
        response = requests.head(
            url,
            headers=HEADERS,
            timeout=5,
            allow_redirects=True
        )

        return response.status_code

    except requests.RequestException:
        return "ERROR"
Enter fullscreen mode Exit fullscreen mode

And then:

for link in internal_links:
    status = check_link(link)

    if status != 200:
        print(f"{status}: {link}")
Enter fullscreen mode Exit fullscreen mode

I wouldn't use this approach to crawl a site with hundreds of thousands of pages.

But for a quick manual audit of a small website, it can be surprisingly effective.

A Useful Browser Console Check

Python isn't always necessary.

Sometimes the browser console is the fastest SEO tool available.

For example, this JavaScript snippet identifies images without an alt attribute:

[...document.querySelectorAll("img")]
  .filter(img => !img.hasAttribute("alt") || !img.alt.trim())
  .map(img => img.src);
Enter fullscreen mode Exit fullscreen mode

Paste it into Chrome DevTools → Console.

You can also find links without meaningful anchor text:

[...document.querySelectorAll("a")]
  .filter(link => !link.innerText.trim())
  .map(link => link.href);
Enter fullscreen mode Exit fullscreen mode

Or inspect heading structure:

[...document.querySelectorAll("h1, h2, h3, h4, h5, h6")]
  .map(heading => ({
    tag: heading.tagName,
    text: heading.innerText.trim()
  }));
Enter fullscreen mode Exit fullscreen mode

These aren't sophisticated tools.

That's exactly why I like them.

They're fast.

Don't Automate the Thinking Part

One mistake I see in technical SEO is treating every warning as a problem that must be fixed.

A missing meta description isn't necessarily catastrophic.

Multiple H1 elements aren't automatically a ranking disaster.

A redirect isn't necessarily wrong.

An image without an alt attribute might even be intentional if the image is purely decorative.

Scripts should help you find things worth investigating.

They shouldn't make the final decision.

Think of automation as a filter:

Website
   ↓
Automated checks
   ↓
Potential issues
   ↓
Manual review
   ↓
Actual SEO tasks
Enter fullscreen mode Exit fullscreen mode

That last manual-review step is still important.

What I'd Add Next

A more complete version of this script could also check:

  • redirect chains
  • canonical mismatches
  • nofollow directives
  • response times
  • duplicate titles
  • Open Graph tags
  • JSON-LD structured data
  • broken internal links
  • image file sizes
  • pages linked with too many parameters
  • missing viewport tags
  • hreflang implementation

You could eventually turn it into a small crawler.

But I wouldn't start there.

Start with the checks you actually need, automate repetitive work, and add functionality only when it solves a real problem.

Final Thought

The most useful technical SEO tools aren't always the biggest ones.

Sometimes a 50-line Python script, Chrome DevTools, and ten minutes of focused investigation can tell you more than another giant exported report.

Automation works best when it removes repetitive work and leaves the interesting part — understanding what actually matters — to the person doing the audit.

Top comments (0)