chore: save iqon static deploy snapshot
This commit is contained in:
Executable
+93
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Create a CSV inventory from the iqon.com page sitemap.
|
||||
|
||||
SPEC: reports/live-captures/2026-05-20/page-sitemap.xml
|
||||
Compliance date: 2026-05-20
|
||||
Launch type: manual SEO inventory generation
|
||||
Registry: iqon sitemap inventory parser
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import sys
|
||||
import urllib.parse
|
||||
import xml.etree.ElementTree as ET
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SITEMAP_NS = {"sm": "http://www.sitemaps.org/schemas/sitemap/0.9"}
|
||||
|
||||
|
||||
def classify_url(url: str, duplicate_count: int) -> tuple[str, str]:
|
||||
parsed = urllib.parse.urlparse(url)
|
||||
decoded_path = urllib.parse.unquote(parsed.path)
|
||||
|
||||
if duplicate_count > 1:
|
||||
return "duplicate", "canonicalize_or_remove_duplicate"
|
||||
if "%" in parsed.path:
|
||||
return "encoded_multilingual", "decide_hreflang_or_noindex_redirect"
|
||||
if decoded_path in {"/homepage/", "/hows-business/"}:
|
||||
return "legacy_home_alias", "redirect_to_root_or_rewrite"
|
||||
if "retail-wholesail" in decoded_path:
|
||||
return "typo_slug", "redirect_to_correct_spelling"
|
||||
if decoded_path == "/services-solutions/":
|
||||
return "thin_landing", "rewrite_as_services_overview"
|
||||
if decoded_path == "/contact/":
|
||||
return "contact", "verify_contact_data_then_keep"
|
||||
if decoded_path == "/":
|
||||
return "homepage", "rewrite_homepage_metadata_and_content"
|
||||
|
||||
return "service_or_content", "review_rewrite_or_merge"
|
||||
|
||||
|
||||
def iter_urls(path: Path) -> list[dict[str, str]]:
|
||||
tree = ET.parse(path)
|
||||
urls: list[dict[str, str]] = []
|
||||
|
||||
for node in tree.findall("sm:url", SITEMAP_NS):
|
||||
loc_node = node.find("sm:loc", SITEMAP_NS)
|
||||
lastmod_node = node.find("sm:lastmod", SITEMAP_NS)
|
||||
if loc_node is None or not loc_node.text:
|
||||
continue
|
||||
|
||||
loc = loc_node.text.strip()
|
||||
parsed = urllib.parse.urlparse(loc)
|
||||
urls.append(
|
||||
{
|
||||
"url": loc,
|
||||
"decoded_path": urllib.parse.unquote(parsed.path),
|
||||
"lastmod": (lastmod_node.text.strip() if lastmod_node is not None and lastmod_node.text else ""),
|
||||
}
|
||||
)
|
||||
|
||||
counts = Counter(item["url"] for item in urls)
|
||||
for item in urls:
|
||||
duplicate_count = counts[item["url"]]
|
||||
item["duplicate_count"] = str(duplicate_count)
|
||||
item["class"], item["recommended_action"] = classify_url(item["url"], duplicate_count)
|
||||
|
||||
return urls
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if len(sys.argv) not in {2, 3}:
|
||||
print("Usage: iqon-sitemap-inventory.py <page-sitemap.xml> [output.csv]", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
rows = iter_urls(Path(sys.argv[1]))
|
||||
output = open(sys.argv[2], "w", newline="", encoding="utf-8") if len(sys.argv) == 3 else sys.stdout
|
||||
writer = csv.DictWriter(
|
||||
output,
|
||||
fieldnames=["url", "decoded_path", "lastmod", "duplicate_count", "class", "recommended_action"],
|
||||
)
|
||||
writer.writeheader()
|
||||
writer.writerows(rows)
|
||||
if output is not sys.stdout:
|
||||
output.close()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user