Python Script to Audit Webpages for SEO & AI Readiness (No API Key Needed)


Interactive Google Colab Practice Notebook

Practice auditing live webpages for SEO meta tags, OpenGraph properties, GA4 tracking codes, JSON-LD schemas, and alt attribute coverage directly inside Google Colab.

View on GitHub Open In Colab

Want to quickly check if a webpage has all the essential SEO and AI-friendly elements without using paid tools or API keys? In this guide, we’ll build and run a Python-based webpage auditor using BeautifulSoup and requests. The script works offline, requires no authentication, and returns practical, actionable results.


Requirements 🔝

Make sure you have the following packages installed:

pip install requests beautifulsoup4 lxml

Python Code: simple_auditor.py 🔝

Here’s the full working script. Change url_to_audit to the webpage you want to check.

# simple_auditor.py
# Minimal, API-key-free webpage auditor with case-insensitive meta extraction.
# Change `url_to_audit` to target another page.

import re
import requests
from urllib.parse import urlparse
from bs4 import BeautifulSoup

# ====== Change this URL to audit another page ======
url_to_audit = "https://www.plus2net.com/python/pandas.php"

UA = "Mozilla/5.0 (compatible; AI-Ready-Auditor/1.1; +https://example.com)"
TIMEOUT = 30

def fetch_html(url: str) -> tuple[int, str]:
    r = requests.get(url, headers={"User-Agent": UA}, timeout=TIMEOUT)
    return r.status_code, r.text

def get_meta_by_name(soup: BeautifulSoup, meta_name: str):
    tag = soup.find("meta", attrs={"name": re.compile(re.escape(meta_name) + "$", re.I)})
    return (tag.get("content") or "").strip() if tag and tag.get("content") else None

def get_meta_by_property(soup: BeautifulSoup, prop_name: str):
    tag = soup.find("meta", attrs={"property": re.compile(re.escape(prop_name) + "$", re.I)})
    return (tag.get("content") or "").strip() if tag and tag.get("content") else None

def extract_schema_flags(json_ld_blocks: list[str]):
    j = "\n".join(json_ld_blocks)
    return "FAQPage" in j, "HowTo" in j, "VideoObject" in j

def analyze(url: str):
    status, html = fetch_html(url)
    soup = BeautifulSoup(html, "lxml")

    title = soup.title.string.strip() if soup.title and soup.title.string else None
    meta_description = get_meta_by_name(soup, "description") or get_meta_by_property(soup, "og:description")
    meta_keywords = get_meta_by_name(soup, "keywords")
    meta_viewport = get_meta_by_name(soup, "viewport")

    h1_tag = soup.find("h1")
    h1 = h1_tag.get_text(strip=True) if h1_tag else None

    canonical_tag = soup.find("link", rel=lambda v: v and v.lower() == "canonical")
    canonical_url = canonical_tag.get("href") if canonical_tag else None

    ga4_ids = list(sorted(set(re.findall("G-[A-Z0-9]{6,12}", html))))
    has_ga4 = bool(ga4_ids)

    og_tags = {
        t.get("property"): t.get("content")
        for t in soup.find_all("meta", attrs={"property": re.compile("^og:", re.I)})
        if t.get("property") and t.get("content")
    }

    imgs = soup.find_all("img")
    images_total = len(imgs)
    images_with_alt = sum(1 for i in imgs if (i.get("alt") or "").strip())
    images_alt_coverage_pct = round((images_with_alt / images_total * 100), 2) if images_total else 0.0

    parsed = urlparse(url)
    base_host = parsed.netloc
    internal_links, external_links = 0, 0
    for a in soup.find_all("a", href=True):
        p = urlparse(a.get("href"))
        if not p.netloc or p.netloc == "" or p.netloc == base_host:
            internal_links += 1
        else:
            external_links += 1

    json_ld_blocks = [t.get_text() for t in soup.find_all("script", type="application/ld+json")]
    has_faq_schema, has_howto_schema, has_video_schema = extract_schema_flags(json_ld_blocks)
    breadcrumbs_present = bool(soup.select('[itemtype*="BreadcrumbList" i], nav.breadcrumb, ol.breadcrumb'))

    root = f"{parsed.scheme}://{parsed.netloc}"
    sitemap_present = False
    try:
        rr = requests.get(f"{root}/robots.txt", headers={"User-Agent": UA}, timeout=10)
        if rr.ok and "sitemap" in rr.text.lower():
            sitemap_present = True
    except Exception:
        pass

    return {
        "url": url,
        "http_status": status,
        "title": title,
        "title_length": len(title) if title else None,
        "meta_description": meta_description,
        "meta_description_length": len(meta_description) if meta_description else None,
        "h1": h1,
        "canonical_url": canonical_url,
        "ga4_ids": ga4_ids,
        "images_total": images_total,
        "images_with_alt": images_with_alt,
        "images_alt_coverage_pct": images_alt_coverage_pct,
        "internal_links": internal_links,
        "external_links": external_links,
        "has_faq_schema": has_faq_schema,
        "has_howto_schema": has_howto_schema,
        "has_videoobject_schema": has_video_schema,
        "breadcrumbs_present": breadcrumbs_present,
        "sitemap_present": sitemap_present
    }

if __name__ == "__main__":
    report = analyze(url_to_audit)
    print(report)

How It Works 🔝

  1. Fetches HTML using the requests library with a customized User-Agent header.
  2. Parses HTML with BeautifulSoup and the lxml parser to locate essential tags case-insensitively.
  3. Compiles audit results into a structured Python dictionary for easy review or logging.

Sample Output 🔝

=== AI-Ready Webpage Audit Summary ===
URL: https://www.plus2net.com/python/pandas.php
HTTP Status: 200
Title (38 chars): Present
Meta Description (45 chars): Present
Canonical URL: https://www.plus2net.com/python/pandas.php
GA4 IDs: ['G-DXKVCW4XVG']
Images with alt: 5/5 (100.0%)
Internal Links: 12 | External Links: 3
FAQ Schema: False
Breadcrumbs: True
Sitemap in robots.txt: True
======================================

Auditing Localhost Webpages 🔝

When testing locally (e.g., using XAMPP or WAMP), you can audit development pages before pushing them live. Simply change the target URL variable to point to your local development environment:

# Change target URL for localhost site auditing
url_to_audit = "http://localhost/plus2net/python/pandas.php"

When auditing local environments, `urlparse` properly identifies `localhost` as the `netloc`, maintaining accurate distinction between local internal links and external references.

To inspect the exact URLs of all internal and external hyperlinked resources, use this focused extraction snippet:

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

url_to_audit = "https://www.plus2net.com/python/pandas.php"
response = requests.get(url_to_audit)
soup = BeautifulSoup(response.text, "html.parser")

parsed_base = urlparse(url_to_audit)
base_host = parsed_base.netloc

internal_links = []
external_links = []

for a in soup.find_all("a", href=True):
    href = a["href"].strip()
    if not href or href.startswith("#") or href.startswith("javascript:"):
        continue
    
    full_url = urljoin(url_to_audit, href)
    parsed_href = urlparse(full_url)
    
    if parsed_href.netloc == base_host:
        internal_links.append(full_url)
    else:
        external_links.append(full_url)

print("--- Internal Links ---")
for link in set(internal_links):
    print(link)

print("\n--- External Links ---")
for link in set(external_links):
    print(link)

Practical Uses of Webpage Auditing 🔝

  • Pre-Publishing SEO Verification: Ensure every new article or tutorial contains valid metadata before pushing to production.
  • AI Readability Checks: Verify schema markup (FAQPage, HowTo) so search engines and AI tools can easily parse and index content.
  • Broken Link & Alt Tag Audits: Keep image accessibility compliance high across large documentation sites.

Common Errors & Troubleshooting 🔝

  • HTTP 403 / Connection Blocked: Ensure your script sends a realistic User-Agent header so web servers do not mistake your script for a malicious scraper.
  • Missing lxml Parser Error: If BeautifulSoup(html, "lxml") throws an error, install lxml via pip install lxml or fallback to 'html.parser'.

Frequently Asked Questions (FAQ) 🔝

Q: Do I need a paid Google API key to run this audit script?
A: No. The script parses the raw HTML directly via BeautifulSoup, requiring zero API keys or rate-limit tokens.

Q: Can I audit local files instead of live URLs?
A: Yes. You can pass local file paths or read HTML file contents into BeautifulSoup instead of making requests.get() calls.


📊 Upgrade Your Webpage Auditing

Want to take your BeautifulSoup auditing to the next level? Check out our Python SQLite Webpage Auditor that stores audit data in a database, allows deeper analysis, and exports reports to Excel for powerful SEO insights.

Learn More →

Create XML Sitemap in Python



Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer