IAMTFRMZA commited on
Commit
9edca94
·
verified ·
1 Parent(s): 615b4c7

Update seo_analyzer_ui.py

Browse files
Files changed (1) hide show
  1. seo_analyzer_ui.py +9 -305
seo_analyzer_ui.py CHANGED
@@ -1,6 +1,7 @@
1
  """
2
- SEO Analyzer UI with Auto Ad Generator Tab
3
  """
 
4
  import gradio as gr
5
  import logging
6
  import json
@@ -18,9 +19,6 @@ from concurrent.futures import ThreadPoolExecutor
18
  from datetime import datetime
19
  import tempfile
20
 
21
- from bs4 import BeautifulSoup
22
- import requests
23
-
24
  from crawler import Crawler
25
  from frontier import URLFrontier
26
  from models import URL, Page
@@ -30,6 +28,7 @@ from dotenv import load_dotenv, find_dotenv
30
 
31
  load_dotenv(find_dotenv())
32
 
 
33
  IS_DEPLOYMENT = os.getenv('DEPLOYMENT', 'false').lower() == 'true'
34
 
35
  # Custom CSS for better styling
@@ -39,26 +38,22 @@ CUSTOM_CSS = """
39
  margin: auto;
40
  padding: 20px;
41
  }
42
-
43
  .header {
44
  text-align: center;
45
  margin-bottom: 2rem;
46
  }
47
-
48
  .header h1 {
49
  color: #2d3748;
50
  font-size: 2.5rem;
51
  font-weight: 700;
52
  margin-bottom: 1rem;
53
  }
54
-
55
  .header p {
56
  color: #4a5568;
57
  font-size: 1.1rem;
58
  max-width: 800px;
59
  margin: 0 auto;
60
  }
61
-
62
  .input-section {
63
  background: #f7fafc;
64
  border-radius: 12px;
@@ -66,7 +61,6 @@ CUSTOM_CSS = """
66
  margin-bottom: 24px;
67
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
68
  }
69
-
70
  .analysis-section {
71
  background: white;
72
  border-radius: 12px;
@@ -74,7 +68,6 @@ CUSTOM_CSS = """
74
  margin-top: 24px;
75
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
76
  }
77
-
78
  .log-section {
79
  font-family: monospace;
80
  background: #1a202c;
@@ -83,7 +76,6 @@ CUSTOM_CSS = """
83
  border-radius: 8px;
84
  margin-top: 24px;
85
  }
86
-
87
  /* Custom styling for inputs */
88
  .input-container {
89
  background: white;
@@ -91,14 +83,12 @@ CUSTOM_CSS = """
91
  border-radius: 8px;
92
  margin-bottom: 16px;
93
  }
94
-
95
  /* Custom styling for the slider */
96
  .slider-container {
97
  padding: 12px;
98
  background: white;
99
  border-radius: 8px;
100
  }
101
-
102
  /* Custom styling for buttons */
103
  .primary-button {
104
  background: #4299e1 !important;
@@ -108,46 +98,38 @@ CUSTOM_CSS = """
108
  font-weight: 600 !important;
109
  transition: all 0.3s ease !important;
110
  }
111
-
112
  .primary-button:hover {
113
  background: #3182ce !important;
114
  transform: translateY(-1px) !important;
115
  }
116
-
117
  /* Markdown output styling */
118
  .markdown-output {
119
  font-family: system-ui, -apple-system, sans-serif;
120
  line-height: 1.6;
121
  }
122
-
123
  .markdown-output h1 {
124
  color: #2d3748;
125
  border-bottom: 2px solid #e2e8f0;
126
  padding-bottom: 0.5rem;
127
  }
128
-
129
  .markdown-output h2 {
130
  color: #4a5568;
131
  margin-top: 2rem;
132
  }
133
-
134
  .markdown-output h3 {
135
  color: #718096;
136
  margin-top: 1.5rem;
137
  }
138
-
139
  /* Progress bar styling */
140
  .progress-bar {
141
  height: 8px !important;
142
  border-radius: 4px !important;
143
  background: #ebf8ff !important;
144
  }
145
-
146
  .progress-bar-fill {
147
  background: #4299e1 !important;
148
  border-radius: 4px !important;
149
  }
150
-
151
  /* Add some spacing between sections */
152
  .gap {
153
  margin: 2rem 0;
@@ -434,10 +416,8 @@ class SEOAnalyzer:
434
  # Format the results
435
  formatted_analysis = f"""
436
  # SEO Analysis Report for {domain}
437
-
438
  ## Overall Analysis
439
  {overall_analysis}
440
-
441
  ## Page-Specific Analyses
442
  """
443
  for page_analysis in page_analyses:
@@ -494,16 +474,13 @@ class SEOAnalyzer:
494
  # Create analysis prompt
495
  prompt = f"""
496
  You are an expert SEO consultant. Analyze this website's SEO based on the crawled data:
497
-
498
  {json.dumps(site_overview, indent=2)}
499
-
500
  Provide a comprehensive SEO analysis including:
501
  1. Overall site structure and navigation
502
  2. Common SEO issues across pages
503
  3. Content quality and optimization
504
  4. Technical SEO recommendations
505
  5. Priority improvements
506
-
507
  Format your response in Markdown.
508
  """
509
 
@@ -533,17 +510,14 @@ Format your response in Markdown.
533
  # Create page analysis prompt
534
  prompt = f"""
535
  Analyze this page's SEO:
536
-
537
  URL: {page['url']}
538
  Metadata: {json.dumps(page['metadata'], indent=2)}
539
-
540
  Provide specific recommendations for:
541
  1. Title and meta description
542
  2. Heading structure
543
  3. Content optimization
544
  4. Internal linking
545
  5. Technical improvements
546
-
547
  Format your response in Markdown.
548
  """
549
 
@@ -609,7 +583,6 @@ def create_ui() -> gr.Interface:
609
  # Create markdown content for the about section
610
  about_markdown = """
611
  # 🔍 SEO Analyzer Pro
612
-
613
  Analyze your website's SEO performance using advanced crawling and AI technology.
614
 
615
  ### Features:
@@ -695,284 +668,15 @@ def create_ui() -> gr.Interface:
695
 
696
  return iface
697
 
698
- # ----- SEO Analyzer UI (as a function) -----
699
- def seo_analyzer_ui():
700
- def analyze(url: str, api_key: str, max_pages: int, progress: gr.Progress = gr.Progress()) -> Tuple[str, str]:
701
- try:
702
- analyzer = SEOAnalyzer(api_key)
703
- analysis, _, logs = analyzer.analyze_website(url, max_pages, progress)
704
- log_output = ""
705
- while not analyzer.log_queue.empty():
706
- try:
707
- log_output += analyzer.log_queue.get_nowait() + "\n"
708
- except queue.Empty:
709
- break
710
- progress(1.0, "Analysis complete")
711
- return analysis, log_output
712
- except Exception as e:
713
- error_msg = f"Error: {str(e)}"
714
- logger.error(error_msg)
715
- return error_msg, error_msg
716
-
717
- about_markdown = """
718
- # 🔍 SEO Analyzer Pro
719
- Analyze your website's SEO performance using advanced crawling and AI technology.
720
- ...
721
- """
722
- with gr.Blocks() as seo_tab:
723
- gr.Markdown(about_markdown)
724
- with gr.Row():
725
- with gr.Column(scale=2):
726
- with gr.Group(elem_classes="input-section"):
727
- gr.Markdown("### 📝 Enter Website Details")
728
- url_input = gr.Textbox(
729
- label="Website URL",
730
- placeholder="https://example.com",
731
- elem_classes="input-container",
732
- info="Enter the full URL of the website you want to analyze (e.g., https://example.com)"
733
- )
734
- api_key = gr.Textbox(
735
- label="OpenAI API Key",
736
- placeholder="sk-...",
737
- type="password",
738
- elem_classes="input-container",
739
- info="Your OpenAI API key is required for AI-powered analysis. Keep this secure!"
740
- )
741
- max_pages = gr.Slider(
742
- minimum=1,
743
- maximum=50,
744
- value=10,
745
- step=1,
746
- label="Maximum Pages to Crawl",
747
- elem_classes="slider-container",
748
- info="Choose how many pages to analyze. More pages = more comprehensive analysis but takes longer"
749
- )
750
- analyze_btn = gr.Button(
751
- "🔍 Analyze Website",
752
- elem_classes="primary-button"
753
- )
754
-
755
- with gr.Row():
756
- with gr.Column():
757
- with gr.Group(elem_classes="analysis-section"):
758
- gr.Markdown("### 📊 Analysis Results")
759
- analysis_output = gr.Markdown(
760
- label="SEO Analysis",
761
- elem_classes="markdown-output"
762
- )
763
- with gr.Row():
764
- with gr.Column():
765
- with gr.Group(elem_classes="log-section"):
766
- gr.Markdown("### 📋 Process Logs")
767
- logs_output = gr.Textbox(
768
- label="Logs",
769
- lines=10,
770
- elem_classes="log-output"
771
- )
772
- analyze_btn.click(
773
- fn=analyze,
774
- inputs=[url_input, api_key, max_pages],
775
- outputs=[analysis_output, logs_output],
776
- )
777
- return seo_tab
778
-
779
- # ---- Auto Ad Generator UI as a function ----
780
- def auto_ad_generator_ui():
781
- openai.api_key = os.getenv("OPENAI_API_KEY")
782
-
783
- def extract_text_from_url(url):
784
- try:
785
- resp = requests.get(url, timeout=30, headers={
786
- "User-Agent": "Mozilla/5.0 (compatible; Bot/1.0)"
787
- })
788
- soup = BeautifulSoup(resp.content, "html.parser")
789
- candidates = soup.find_all(['h1','h2','h3','h4','p','span','li'])
790
- text = ' '.join([c.get_text(strip=True) for c in candidates])
791
- text = text[:4000]
792
- if len(text) < 100:
793
- raise ValueError("Could not extract enough content (site may require JavaScript). Please enter keywords manually.")
794
- return text
795
- except Exception as e:
796
- raise ValueError(f"URL extraction error: {e}")
797
-
798
- def extract_keywords(text):
799
- prompt = f"""
800
- Extract up to 10 concise, relevant SEO keywords suitable for an automotive advertisement from the following content:
801
- {text}
802
- Keywords:
803
- """
804
- response = openai.ChatCompletion.create(
805
- model="gpt-4",
806
- messages=[{"role": "user", "content": prompt}],
807
- temperature=0.6,
808
- max_tokens=100
809
- )
810
- output = response.choices[0].message.content.strip()
811
- if ',' in output:
812
- keywords = output.split(',')
813
- else:
814
- keywords = output.split('\n')
815
- return [kw.strip() for kw in keywords if kw.strip()]
816
-
817
- def generate_ad_copy(platform, keywords):
818
- prompt = f"""
819
- Create a compelling, SEO-optimized {platform} ad using these keywords: {', '.join(keywords)}.
820
- Include a clear and enticing call-to-action.
821
- """
822
- response = openai.ChatCompletion.create(
823
- model="gpt-4",
824
- messages=[{"role": "user", "content": prompt}],
825
- temperature=0.7,
826
- max_tokens=300
827
- )
828
- return response.choices[0].message.content.strip()
829
-
830
- def generate_ad_image(keywords):
831
- kw_str = ", ".join(keywords)
832
- image_prompt = (
833
- f"High-quality, photorealistic automotive ad photo of a luxury car. "
834
- f"Clean background, professional lighting, stylish dealership setting. "
835
- f"Keywords: {kw_str}. Room for text overlay, wide format, visually appealing."
836
- )
837
- response = openai.Image.create(
838
- prompt=image_prompt,
839
- n=1,
840
- size="512x512"
841
- )
842
- image_url = response["data"][0]["url"]
843
- img_data = requests.get(image_url).content
844
- img_file = "generated_ad_image.png"
845
- with open(img_file, "wb") as f:
846
- f.write(img_data)
847
- return img_file
848
-
849
- def platform_html(platform, ad_text):
850
- if platform == "Facebook":
851
- color = "#1877F2"
852
- icon = "🌐"
853
- elif platform == "Instagram":
854
- color = "linear-gradient(90deg, #f58529 0%, #dd2a7b 50%, #8134af 100%)"
855
- icon = "📸"
856
- elif platform == "X (Twitter)":
857
- color = "#14171A"
858
- icon = "🐦"
859
- else: # Google Search
860
- color = "#4285F4"
861
- icon = "🔍"
862
-
863
- if platform == "Instagram":
864
- content = f"""
865
- <div style="background: {color}; padding: 2px; border-radius: 12px; margin-bottom:16px;">
866
- <div style="background: white; color: #333; padding: 18px 20px; border-radius: 10px;">
867
- <span style="font-size: 1.5em;">{icon} <b>{platform}</b></span>
868
- <div style="margin-top: 12px; font-size: 1.1em; line-height:1.6;">{ad_text}</div>
869
- </div>
870
- </div>
871
- """
872
- else:
873
- content = f"""
874
- <div style="background: {color}; color: white; padding: 18px 20px; border-radius: 12px; margin-bottom:16px; min-height: 120px;">
875
- <span style="font-size: 1.5em;">{icon} <b>{platform}</b></span>
876
- <div style="margin-top: 12px; font-size: 1.1em; line-height:1.6;">{ad_text}</div>
877
- </div>
878
- """
879
- return content
880
-
881
- def main_workflow(input_mode, url_or_keywords):
882
- error = None
883
- keywords = []
884
- ad_copies = {}
885
- image_path = None
886
-
887
- if input_mode == "URL":
888
- try:
889
- text = extract_text_from_url(url_or_keywords)
890
- keywords = extract_keywords(text)
891
- except Exception as e:
892
- return None, None, None, f"{e}"
893
- else:
894
- keywords = [kw.strip() for kw in url_or_keywords.split(",") if kw.strip()]
895
- if not keywords:
896
- return None, None, None, "Please provide at least one keyword."
897
- # Generate ad copies
898
- platforms = ["Facebook", "Instagram", "X (Twitter)", "Google Search"]
899
- for platform in platforms:
900
- ad_copies[platform] = generate_ad_copy(platform, keywords)
901
- # Generate image
902
- try:
903
- image_path = generate_ad_image(keywords)
904
- except Exception as e:
905
- error = f"Image generation error: {e}"
906
-
907
- # Save ads to txt
908
- output_txt = "generated_ads.txt"
909
- with open(output_txt, "w", encoding="utf-8") as f:
910
- for platform, content in ad_copies.items():
911
- f.write(f"--- {platform} Ad Copy ---\n{content}\n\n")
912
- return keywords, ad_copies, image_path, error
913
-
914
- def run_space(input_mode, url, keywords):
915
- url_or_keywords = url if input_mode == "URL" else keywords
916
- keywords, ad_copies, image_path, error = main_workflow(input_mode, url_or_keywords)
917
- ad_previews = ""
918
- if ad_copies:
919
- for platform, ad in ad_copies.items():
920
- ad_previews += platform_html(platform, ad)
921
- return (
922
- keywords,
923
- ad_previews,
924
- image_path,
925
- "generated_ads.txt" if ad_copies else None,
926
- error
927
- )
928
-
929
- with gr.Blocks() as ad_tab:
930
- gr.Markdown("# 🚗 Auto Ad Generator\nPaste a car listing URL **or** enter your own keywords, then preview AI-generated ads for each social media platform, plus an auto-generated image!")
931
- input_mode = gr.Radio(["URL", "Keywords"], value="URL", label="Input Type")
932
- url_input = gr.Textbox(label="Listing URL", placeholder="https://www.cars.com/listing/...", visible=True)
933
- kw_input = gr.Textbox(label="Manual Keywords (comma separated)", placeholder="e.g. BMW, used car, sunroof", visible=False)
934
- submit_btn = gr.Button("Generate Ads")
935
-
936
- gr.Markdown("## Keywords")
937
- kw_out = gr.JSON(label="Extracted/Provided Keywords")
938
-
939
- gr.Markdown("## Ad Copy Previews")
940
- ad_out = gr.HTML(label="Ad Copy Preview")
941
-
942
- gr.Markdown("## Generated Ad Image")
943
- img_out = gr.Image(label="Generated Ad Image", type="filepath")
944
-
945
- gr.Markdown("## Download Ad Copies")
946
- file_out = gr.File(label="Download TXT")
947
-
948
- err_out = gr.Textbox(label="Errors", interactive=False)
949
-
950
- def show_hide_fields(choice):
951
- return (
952
- gr.update(visible=choice == "URL"),
953
- gr.update(visible=choice == "Keywords"),
954
- )
955
-
956
- input_mode.change(show_hide_fields, input_mode, [url_input, kw_input])
957
-
958
- submit_btn.click(
959
- run_space,
960
- inputs=[input_mode, url_input, kw_input],
961
- outputs=[kw_out, ad_out, img_out, file_out, err_out]
962
- )
963
- return ad_tab
964
-
965
- # ---- Main App: Two Tabs ----
966
  if __name__ == "__main__":
 
967
  os.makedirs(config.STORAGE_PATH, exist_ok=True)
968
- with gr.Blocks(css=CUSTOM_CSS) as demo:
969
- with gr.Tab("SEO Analyzer"):
970
- seo_analyzer_ui()
971
- with gr.Tab("Auto Ad Generator"):
972
- auto_ad_generator_ui()
973
- demo.launch(
974
  share=False,
975
  server_name="0.0.0.0",
976
  show_api=False,
977
  show_error=True,
978
- )
 
1
  """
2
+ SEO Analyzer UI using Gradio, Web Crawler, and OpenAI
3
  """
4
+
5
  import gradio as gr
6
  import logging
7
  import json
 
19
  from datetime import datetime
20
  import tempfile
21
 
 
 
 
22
  from crawler import Crawler
23
  from frontier import URLFrontier
24
  from models import URL, Page
 
28
 
29
  load_dotenv(find_dotenv())
30
 
31
+ # Check if we're in deployment mode (e.g., Hugging Face Spaces)
32
  IS_DEPLOYMENT = os.getenv('DEPLOYMENT', 'false').lower() == 'true'
33
 
34
  # Custom CSS for better styling
 
38
  margin: auto;
39
  padding: 20px;
40
  }
 
41
  .header {
42
  text-align: center;
43
  margin-bottom: 2rem;
44
  }
 
45
  .header h1 {
46
  color: #2d3748;
47
  font-size: 2.5rem;
48
  font-weight: 700;
49
  margin-bottom: 1rem;
50
  }
 
51
  .header p {
52
  color: #4a5568;
53
  font-size: 1.1rem;
54
  max-width: 800px;
55
  margin: 0 auto;
56
  }
 
57
  .input-section {
58
  background: #f7fafc;
59
  border-radius: 12px;
 
61
  margin-bottom: 24px;
62
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
63
  }
 
64
  .analysis-section {
65
  background: white;
66
  border-radius: 12px;
 
68
  margin-top: 24px;
69
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
70
  }
 
71
  .log-section {
72
  font-family: monospace;
73
  background: #1a202c;
 
76
  border-radius: 8px;
77
  margin-top: 24px;
78
  }
 
79
  /* Custom styling for inputs */
80
  .input-container {
81
  background: white;
 
83
  border-radius: 8px;
84
  margin-bottom: 16px;
85
  }
 
86
  /* Custom styling for the slider */
87
  .slider-container {
88
  padding: 12px;
89
  background: white;
90
  border-radius: 8px;
91
  }
 
92
  /* Custom styling for buttons */
93
  .primary-button {
94
  background: #4299e1 !important;
 
98
  font-weight: 600 !important;
99
  transition: all 0.3s ease !important;
100
  }
 
101
  .primary-button:hover {
102
  background: #3182ce !important;
103
  transform: translateY(-1px) !important;
104
  }
 
105
  /* Markdown output styling */
106
  .markdown-output {
107
  font-family: system-ui, -apple-system, sans-serif;
108
  line-height: 1.6;
109
  }
 
110
  .markdown-output h1 {
111
  color: #2d3748;
112
  border-bottom: 2px solid #e2e8f0;
113
  padding-bottom: 0.5rem;
114
  }
 
115
  .markdown-output h2 {
116
  color: #4a5568;
117
  margin-top: 2rem;
118
  }
 
119
  .markdown-output h3 {
120
  color: #718096;
121
  margin-top: 1.5rem;
122
  }
 
123
  /* Progress bar styling */
124
  .progress-bar {
125
  height: 8px !important;
126
  border-radius: 4px !important;
127
  background: #ebf8ff !important;
128
  }
 
129
  .progress-bar-fill {
130
  background: #4299e1 !important;
131
  border-radius: 4px !important;
132
  }
 
133
  /* Add some spacing between sections */
134
  .gap {
135
  margin: 2rem 0;
 
416
  # Format the results
417
  formatted_analysis = f"""
418
  # SEO Analysis Report for {domain}
 
419
  ## Overall Analysis
420
  {overall_analysis}
 
421
  ## Page-Specific Analyses
422
  """
423
  for page_analysis in page_analyses:
 
474
  # Create analysis prompt
475
  prompt = f"""
476
  You are an expert SEO consultant. Analyze this website's SEO based on the crawled data:
 
477
  {json.dumps(site_overview, indent=2)}
 
478
  Provide a comprehensive SEO analysis including:
479
  1. Overall site structure and navigation
480
  2. Common SEO issues across pages
481
  3. Content quality and optimization
482
  4. Technical SEO recommendations
483
  5. Priority improvements
 
484
  Format your response in Markdown.
485
  """
486
 
 
510
  # Create page analysis prompt
511
  prompt = f"""
512
  Analyze this page's SEO:
 
513
  URL: {page['url']}
514
  Metadata: {json.dumps(page['metadata'], indent=2)}
 
515
  Provide specific recommendations for:
516
  1. Title and meta description
517
  2. Heading structure
518
  3. Content optimization
519
  4. Internal linking
520
  5. Technical improvements
 
521
  Format your response in Markdown.
522
  """
523
 
 
583
  # Create markdown content for the about section
584
  about_markdown = """
585
  # 🔍 SEO Analyzer Pro
 
586
  Analyze your website's SEO performance using advanced crawling and AI technology.
587
 
588
  ### Features:
 
668
 
669
  return iface
670
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
671
  if __name__ == "__main__":
672
+ # Create base storage directory if it doesn't exist
673
  os.makedirs(config.STORAGE_PATH, exist_ok=True)
674
+
675
+ # Create and launch UI
676
+ ui = create_ui()
677
+ ui.launch(
 
 
678
  share=False,
679
  server_name="0.0.0.0",
680
  show_api=False,
681
  show_error=True,
682
+ )