File size: 4,952 Bytes
0cdb73b
c11950c
 
 
dbac20f
c11950c
 
 
dbac20f
c11950c
 
 
ba6e005
 
c11950c
 
 
ba6e005
c11950c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59b0bed
c11950c
 
 
 
 
 
ba6e005
59b0bed
c11950c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbac20f
c11950c
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import requests
from bs4 import BeautifulSoup
from urllib.parse import urlparse, urljoin
import gradio as gr

def seo_check(url):
    report = []
    suggestions = []

    # Ensure HTTPS
    if not url.startswith("http"):
        url = "https://" + url

    try:
        response = requests.get(url, timeout=10)
        response.raise_for_status()
        html = response.text
    except Exception as e:
        return f"❌ Error accessing URL: {e}", ""

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

    # Title Tag
    title = soup.title.string.strip() if soup.title else ""
    if not title:
        report.append("❌ Missing <title> tag.")
        suggestions.append("Add a <title> tag that describes your page in 50–60 characters.")
    elif len(title) > 70:
        report.append("⚠️ Title is too long.")
        suggestions.append("Keep title under 70 characters.")

    # Meta Description
    desc_tag = soup.find("meta", attrs={"name": "description"})
    desc = desc_tag["content"].strip() if desc_tag and desc_tag.get("content") else ""
    if not desc:
        report.append("❌ Missing meta description.")
        suggestions.append("Add a <meta name='description'> summarizing the page.")
    elif len(desc) > 160:
        report.append("⚠️ Meta description is too long.")
        suggestions.append("Keep meta descriptions under 160 characters.")

    # Canonical Tag
    canonical = soup.find("link", rel="canonical")
    if not canonical:
        report.append("❌ Missing canonical link.")
        suggestions.append("Add a <link rel='canonical'> to avoid duplicate content.")

    # H1 Tag
    h1_tags = soup.find_all("h1")
    if len(h1_tags) != 1:
        report.append(f"⚠️ Found {len(h1_tags)} <h1> tags.")
        suggestions.append("Use exactly one <h1> tag for SEO clarity.")

    # Mobile viewport
    viewport = soup.find("meta", attrs={"name": "viewport"})
    if not viewport:
        report.append("⚠️ No viewport meta tag.")
        suggestions.append("Add a viewport meta tag for mobile responsiveness.")

    # HTTPS check
    if not url.startswith("https://"):
        report.append("⚠️ URL is not secure (no HTTPS).")
        suggestions.append("Install SSL and redirect HTTP to HTTPS.")

    # Robots.txt and sitemap.xml
    parsed = urlparse(url)
    base = f"{parsed.scheme}://{parsed.netloc}"
    robots_url = urljoin(base, "/robots.txt")
    sitemap_url = urljoin(base, "/sitemap.xml")
    try:
        r1 = requests.get(robots_url)
        if r1.status_code != 200:
            report.append("❌ robots.txt not found.")
            suggestions.append("Create a robots.txt to guide search bots.")
    except:
        report.append("❌ Could not access robots.txt.")

    try:
        r2 = requests.get(sitemap_url)
        if r2.status_code != 200:
            report.append("❌ sitemap.xml not found.")
            suggestions.append("Add sitemap.xml for better crawling.")
    except:
        report.append("❌ Could not access sitemap.xml.")

    # Open Graph Tags
    og_title = soup.find("meta", property="og:title")
    if not og_title:
        report.append("⚠️ Missing Open Graph (og:title).")
        suggestions.append("Add OG tags to improve sharing on social media.")

    # Image alt text
    images = soup.find_all("img")
    alt_missing = [img for img in images if not img.get("alt")]
    if alt_missing:
        report.append(f"⚠️ {len(alt_missing)} images missing alt text.")
        suggestions.append("Add descriptive alt attributes to all images.")

    # Internal and external links
    links = soup.find_all("a", href=True)
    internal = 0
    external = 0
    for link in links:
        href = link['href']
        if parsed.netloc in href:
            internal += 1
        elif href.startswith("http"):
            external += 1
    report.append(f"ℹ️ Internal Links: {internal} | External Links: {external}")
    suggestions.append("Ensure most important links are internal. Check broken links.")

    # Keyword density (basic)
    body_text = soup.get_text().lower()
    words = body_text.split()
    word_count = len(words)
    keyword = parsed.netloc.replace("www.", "").split(".")[0]
    keyword_freq = words.count(keyword)
    density = (keyword_freq / word_count) * 100 if word_count else 0
    report.append(f"ℹ️ Keyword '{keyword}' appears {keyword_freq} times ({density:.2f}% density)")
    if density < 0.5:
        suggestions.append("Consider using your main keyword more often (target 1–2%).")

    return "\n".join(report), "\n".join(suggestions)

# Gradio UI
gr.Interface(
    fn=seo_check,
    inputs=gr.Textbox(label="Enter Website URL"),
    outputs=[
        gr.Textbox(label="SEO Report", lines=15),
        gr.Textbox(label="Suggestions & Fixes", lines=15)
    ],
    title="SEO Website Checker",
    description="Analyze your website's SEO like Sitechecker.pro & SEOSiteCheckup, with clear solutions!"
).launch()