ahmedumeraziz commited on
Commit
4975741
·
verified ·
1 Parent(s): 844d184

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +110 -34
app.py CHANGED
@@ -1,59 +1,135 @@
1
  import requests
2
  from bs4 import BeautifulSoup
 
3
  import gradio as gr
4
 
5
- def check_seo(url):
 
 
 
 
 
 
 
6
  try:
7
  response = requests.get(url, timeout=10)
8
  response.raise_for_status()
 
9
  except Exception as e:
10
- return f"❌ Error accessing URL: {str(e)}", ""
11
 
12
- soup = BeautifulSoup(response.text, "html.parser")
13
- report = []
14
- fix_guide = []
15
 
16
- # Title tag
17
- title = soup.title.string if soup.title else None
18
  if not title:
19
  report.append("❌ Missing <title> tag.")
20
- fix_guide.append("Add a descriptive <title> tag with relevant keywords.")
21
- elif len(title) < 10 or len(title) > 70:
22
- report.append("⚠️ <title> tag length is not optimal.")
23
- fix_guide.append("Keep your title between 50-60 characters.")
24
-
25
- # Meta description
26
- meta_desc = soup.find("meta", attrs={"name": "description"})
27
- if not meta_desc or not meta_desc.get("content"):
 
28
  report.append("❌ Missing meta description.")
29
- fix_guide.append("Add a <meta name='description'> with a summary of your page.")
30
- elif len(meta_desc["content"]) < 50 or len(meta_desc["content"]) > 160:
31
- report.append("⚠️ Meta description length is not optimal.")
32
- fix_guide.append("Keep meta descriptions between 150-160 characters.")
 
 
 
 
 
 
33
 
34
- # Headings
35
  h1_tags = soup.find_all("h1")
36
  if len(h1_tags) != 1:
37
  report.append(f"⚠️ Found {len(h1_tags)} <h1> tags.")
38
- fix_guide.append("Use exactly one <h1> tag to define the main heading.")
 
 
 
 
 
 
39
 
40
- # Images with alt
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  images = soup.find_all("img")
42
- missing_alt = [img for img in images if not img.get("alt")]
43
- if missing_alt:
44
- report.append(f"⚠️ {len(missing_alt)} image(s) missing alt attribute.")
45
- fix_guide.append("Add descriptive alt text to all images.")
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- # Mobile-friendliness & Page speed (basic hints, real check would require APIs)
48
- report.append("ℹ️ For mobile-friendliness and performance, use tools like Google PageSpeed Insights.")
49
- fix_guide.append("Use https://pagespeed.web.dev/ for full performance and mobile checks.")
 
 
 
 
 
 
 
50
 
51
- return "\n".join(report), "\n".join(fix_guide)
52
 
 
53
  gr.Interface(
54
- fn=check_seo,
55
  inputs=gr.Textbox(label="Enter Website URL"),
56
- outputs=[gr.Textbox(label="SEO Report"), gr.Textbox(label="Fix Suggestions")],
57
- title="Website SEO Check",
58
- description="Checks basic SEO elements of a given URL and provides recommendations."
 
 
 
59
  ).launch()
 
1
  import requests
2
  from bs4 import BeautifulSoup
3
+ from urllib.parse import urlparse, urljoin
4
  import gradio as gr
5
 
6
+ def seo_check(url):
7
+ report = []
8
+ suggestions = []
9
+
10
+ # Ensure HTTPS
11
+ if not url.startswith("http"):
12
+ url = "https://" + url
13
+
14
  try:
15
  response = requests.get(url, timeout=10)
16
  response.raise_for_status()
17
+ html = response.text
18
  except Exception as e:
19
+ return f"❌ Error accessing URL: {e}", ""
20
 
21
+ soup = BeautifulSoup(html, "html.parser")
 
 
22
 
23
+ # Title Tag
24
+ title = soup.title.string.strip() if soup.title else ""
25
  if not title:
26
  report.append("❌ Missing <title> tag.")
27
+ suggestions.append("Add a <title> tag that describes your page in 50–60 characters.")
28
+ elif len(title) > 70:
29
+ report.append("⚠️ Title is too long.")
30
+ suggestions.append("Keep title under 70 characters.")
31
+
32
+ # Meta Description
33
+ desc_tag = soup.find("meta", attrs={"name": "description"})
34
+ desc = desc_tag["content"].strip() if desc_tag and desc_tag.get("content") else ""
35
+ if not desc:
36
  report.append("❌ Missing meta description.")
37
+ suggestions.append("Add a <meta name='description'> summarizing the page.")
38
+ elif len(desc) > 160:
39
+ report.append("⚠️ Meta description is too long.")
40
+ suggestions.append("Keep meta descriptions under 160 characters.")
41
+
42
+ # Canonical Tag
43
+ canonical = soup.find("link", rel="canonical")
44
+ if not canonical:
45
+ report.append("❌ Missing canonical link.")
46
+ suggestions.append("Add a <link rel='canonical'> to avoid duplicate content.")
47
 
48
+ # H1 Tag
49
  h1_tags = soup.find_all("h1")
50
  if len(h1_tags) != 1:
51
  report.append(f"⚠️ Found {len(h1_tags)} <h1> tags.")
52
+ suggestions.append("Use exactly one <h1> tag for SEO clarity.")
53
+
54
+ # Mobile viewport
55
+ viewport = soup.find("meta", attrs={"name": "viewport"})
56
+ if not viewport:
57
+ report.append("⚠️ No viewport meta tag.")
58
+ suggestions.append("Add a viewport meta tag for mobile responsiveness.")
59
 
60
+ # HTTPS check
61
+ if not url.startswith("https://"):
62
+ report.append("⚠️ URL is not secure (no HTTPS).")
63
+ suggestions.append("Install SSL and redirect HTTP to HTTPS.")
64
+
65
+ # Robots.txt and sitemap.xml
66
+ parsed = urlparse(url)
67
+ base = f"{parsed.scheme}://{parsed.netloc}"
68
+ robots_url = urljoin(base, "/robots.txt")
69
+ sitemap_url = urljoin(base, "/sitemap.xml")
70
+ try:
71
+ r1 = requests.get(robots_url)
72
+ if r1.status_code != 200:
73
+ report.append("❌ robots.txt not found.")
74
+ suggestions.append("Create a robots.txt to guide search bots.")
75
+ except:
76
+ report.append("❌ Could not access robots.txt.")
77
+
78
+ try:
79
+ r2 = requests.get(sitemap_url)
80
+ if r2.status_code != 200:
81
+ report.append("❌ sitemap.xml not found.")
82
+ suggestions.append("Add sitemap.xml for better crawling.")
83
+ except:
84
+ report.append("❌ Could not access sitemap.xml.")
85
+
86
+ # Open Graph Tags
87
+ og_title = soup.find("meta", property="og:title")
88
+ if not og_title:
89
+ report.append("⚠️ Missing Open Graph (og:title).")
90
+ suggestions.append("Add OG tags to improve sharing on social media.")
91
+
92
+ # Image alt text
93
  images = soup.find_all("img")
94
+ alt_missing = [img for img in images if not img.get("alt")]
95
+ if alt_missing:
96
+ report.append(f"⚠️ {len(alt_missing)} images missing alt text.")
97
+ suggestions.append("Add descriptive alt attributes to all images.")
98
+
99
+ # Internal and external links
100
+ links = soup.find_all("a", href=True)
101
+ internal = 0
102
+ external = 0
103
+ for link in links:
104
+ href = link['href']
105
+ if parsed.netloc in href:
106
+ internal += 1
107
+ elif href.startswith("http"):
108
+ external += 1
109
+ report.append(f"ℹ️ Internal Links: {internal} | External Links: {external}")
110
+ suggestions.append("Ensure most important links are internal. Check broken links.")
111
 
112
+ # Keyword density (basic)
113
+ body_text = soup.get_text().lower()
114
+ words = body_text.split()
115
+ word_count = len(words)
116
+ keyword = parsed.netloc.replace("www.", "").split(".")[0]
117
+ keyword_freq = words.count(keyword)
118
+ density = (keyword_freq / word_count) * 100 if word_count else 0
119
+ report.append(f"ℹ️ Keyword '{keyword}' appears {keyword_freq} times ({density:.2f}% density)")
120
+ if density < 0.5:
121
+ suggestions.append("Consider using your main keyword more often (target 1–2%).")
122
 
123
+ return "\n".join(report), "\n".join(suggestions)
124
 
125
+ # Gradio UI
126
  gr.Interface(
127
+ fn=seo_check,
128
  inputs=gr.Textbox(label="Enter Website URL"),
129
+ outputs=[
130
+ gr.Textbox(label="SEO Report", lines=15),
131
+ gr.Textbox(label="Suggestions & Fixes", lines=15)
132
+ ],
133
+ title="SEO Website Checker",
134
+ description="Analyze your website's SEO like Sitechecker.pro & SEOSiteCheckup, with clear solutions!"
135
  ).launch()