Spaces:
Sleeping
Sleeping
Update seo_analyzer_ui.py
Browse files- seo_analyzer_ui.py +9 -305
seo_analyzer_ui.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
"""
|
2 |
-
SEO Analyzer UI
|
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 |
-
|
969 |
-
|
970 |
-
|
971 |
-
|
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 |
+
)
|