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

Update seo_analyzer_ui.py

Browse files
Files changed (1) hide show
  1. seo_analyzer_ui.py +279 -9
seo_analyzer_ui.py CHANGED
@@ -1,7 +1,6 @@
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,6 +18,9 @@ from concurrent.futures import ThreadPoolExecutor
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,7 +30,6 @@ from dotenv import load_dotenv, find_dotenv
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
@@ -694,15 +695,284 @@ def create_ui() -> gr.Interface:
694
 
695
  return iface
696
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
697
  if __name__ == "__main__":
698
- # Create base storage directory if it doesn't exist
699
  os.makedirs(config.STORAGE_PATH, exist_ok=True)
700
-
701
- # Create and launch UI
702
- ui = create_ui()
703
- ui.launch(
 
 
704
  share=False,
705
  server_name="0.0.0.0",
706
  show_api=False,
707
  show_error=True,
708
- )
 
1
  """
2
+ SEO Analyzer UI with Auto Ad Generator Tab
3
  """
 
4
  import gradio as gr
5
  import logging
6
  import json
 
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
 
31
  load_dotenv(find_dotenv())
32
 
 
33
  IS_DEPLOYMENT = os.getenv('DEPLOYMENT', 'false').lower() == 'true'
34
 
35
  # Custom CSS for better styling
 
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
+ )