Spaces:
Sleeping
Sleeping
Commit ·
663cb99
0
Parent(s):
Initial commit of SEO Analyzer FastAPI
Browse files- .gitignore +5 -0
- PROJECT_DOCS.md +87 -0
- main.py +30 -0
- models/schemas.py +23 -0
- requirements.txt +5 -0
- services/analyzer.py +84 -0
.gitignore
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__/
|
| 2 |
+
*.pyc
|
| 3 |
+
.env
|
| 4 |
+
venv/
|
| 5 |
+
.DS_Store
|
PROJECT_DOCS.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 📘 SEO Keyword Analyzer API - Project Documentation
|
| 2 |
+
|
| 3 |
+
## 1. Project Overview
|
| 4 |
+
This project is a high-performance **Microservice** built with **FastAPI**. Its purpose is to act as an intelligent SEO (Search Engine Optimization) consultant. It accepts raw text (like a video topic or blog idea) and uses Google's **Gemini Generative AI** to output a structured strategy containing keywords, viral hashtags, and target audience analysis.
|
| 5 |
+
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
## 2. Directory Structure & File Explanation
|
| 9 |
+
|
| 10 |
+
```
|
| 11 |
+
SEO_Analyzer_FastAPI/
|
| 12 |
+
├── main.py # 🚀 The Entry Point
|
| 13 |
+
├── requirements.txt # 📦 Dependencies
|
| 14 |
+
├── models/
|
| 15 |
+
│ └── schemas.py # 🏗️ Data Structure (Validation)
|
| 16 |
+
└── services/
|
| 17 |
+
└── analyzer.py # 🧠 The Brain (AI Logic)
|
| 18 |
+
```
|
| 19 |
+
|
| 20 |
+
### A. `main.py` (The Traffic Controller)
|
| 21 |
+
This is where the application starts.
|
| 22 |
+
- **FastAPI App**: Initializes the web server.
|
| 23 |
+
- **Endpoint `/analyze-seo`**: A specific "door" where users send data.
|
| 24 |
+
- **Logic**: When a request comes in, it checks the data format and passes it to the *Brain* (`analyzer.py`).
|
| 25 |
+
|
| 26 |
+
### B. `models/schemas.py` (The Blueprint)
|
| 27 |
+
This file defines exactly what data looks like using **Pydantic**.
|
| 28 |
+
- `SEORequest`: Ensures the user sends a JSON with a `"content"` field (String).
|
| 29 |
+
- `SEOResponse`: Ensures the API returns a standard format (Keywords, Hashtags, Tips) so the frontend never crashes.
|
| 30 |
+
|
| 31 |
+
### C. `services/analyzer.py` (The Brain)
|
| 32 |
+
This contains the core business logic.
|
| 33 |
+
- **Loading Environment**: Finds your `.env` file to get the Secret Key.
|
| 34 |
+
- **Prompt Engineering**: Constructs a strict prompt telling the AI to act as an "SEO Strategist".
|
| 35 |
+
- **AI Call**: Sends the prompt to Google Gemini.
|
| 36 |
+
- **JSON Parsing**: Takes the AI's raw text response and converts it into a clean Python Dictionary.
|
| 37 |
+
|
| 38 |
+
---
|
| 39 |
+
|
| 40 |
+
## 3. How the Logic Works (A to Z)
|
| 41 |
+
|
| 42 |
+
1. **Request**: You send `POST /analyze-seo` with `{"content": "youtube automation"}`.
|
| 43 |
+
2. **Validation**: `main.py` uses `schemas.py` to confirm you sent text, not a number or empty file.
|
| 44 |
+
3. **Processing**:
|
| 45 |
+
- The code calls `analyze_seo_content("youtube automation")`.
|
| 46 |
+
- It builds a prompt: *"Analyze 'youtube automation' and give me high-volume keywords..."*
|
| 47 |
+
4. **AI Interaction**:
|
| 48 |
+
- The system tries to connect to `gemini-2.0-flash`.
|
| 49 |
+
- If that model is busy or broken (404), it automatically loops to the next backup model (`gemini-flash-latest`).
|
| 50 |
+
- This "Fallback Loop" ensures high reliability.
|
| 51 |
+
5. **Response Handling**:
|
| 52 |
+
- The AI returns a JSON-formatted string.
|
| 53 |
+
- Python parses this string.
|
| 54 |
+
- The function returns the data.
|
| 55 |
+
6. **Response**: You receive the JSON data with code `200 OK`.
|
| 56 |
+
|
| 57 |
+
---
|
| 58 |
+
|
| 59 |
+
## 4. How to Use & Run (Step-by-Step)
|
| 60 |
+
|
| 61 |
+
### Step 1: Install Requirements
|
| 62 |
+
Open your terminal in the project folder and run:
|
| 63 |
+
```bash
|
| 64 |
+
pip install -r requirements.txt
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
### Step 2: Run the Server
|
| 68 |
+
Start the API server (Uvicorn):
|
| 69 |
+
```bash
|
| 70 |
+
python -m uvicorn main:app --reload --host 0.0.0.0 --port 8000
|
| 71 |
+
```
|
| 72 |
+
- `host 0.0.0.0`: Allows it to work on LAN (WiFi).
|
| 73 |
+
- `reload`: Updates automatically when you save code.
|
| 74 |
+
|
| 75 |
+
### Step 3: Test with Swagger UI
|
| 76 |
+
1. Open your browser to: **[http://localhost:8000/docs](http://localhost:8000/docs)**
|
| 77 |
+
2. Click **POST /analyze-seo**.
|
| 78 |
+
3. Click **Try it out**.
|
| 79 |
+
4. Enter your topic in the "content" field.
|
| 80 |
+
5. Click **Execute**.
|
| 81 |
+
6. See the results below!
|
| 82 |
+
|
| 83 |
+
---
|
| 84 |
+
|
| 85 |
+
## 5. Troubleshooting
|
| 86 |
+
- **Error 500 "GEMINI_API_KEY not found"**: correct: Ensure your `.env` file is in the parent directory or properly loaded in `analyzer.py`.
|
| 87 |
+
- **404 Model Not Found**: The code handles this by trying multiple models, but ensure `MODELS_FALLBACK` contains valid model names correctly.
|
main.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, HTTPException
|
| 2 |
+
from models.schemas import SEORequest, SEOResponse
|
| 3 |
+
from services.analyzer import analyze_seo_content
|
| 4 |
+
import uvicorn
|
| 5 |
+
import os
|
| 6 |
+
|
| 7 |
+
app = FastAPI(
|
| 8 |
+
title="SEO Keyword Analyzer API",
|
| 9 |
+
description="Professional SEO Analysis using Generative AI",
|
| 10 |
+
version="1.0.0"
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
@app.get("/")
|
| 14 |
+
def read_root():
|
| 15 |
+
return {"status": "active", "service": "SEO Analyzer API"}
|
| 16 |
+
|
| 17 |
+
@app.post("/analyze-seo", response_model=SEOResponse)
|
| 18 |
+
def analyze_seo(request: SEORequest):
|
| 19 |
+
"""
|
| 20 |
+
Analyzes the provided content and returns a detailed SEO strategy.
|
| 21 |
+
"""
|
| 22 |
+
result = analyze_seo_content(request.content)
|
| 23 |
+
|
| 24 |
+
if "error" in result and result["error"]:
|
| 25 |
+
raise HTTPException(status_code=500, detail=result["error"])
|
| 26 |
+
|
| 27 |
+
return result
|
| 28 |
+
|
| 29 |
+
if __name__ == "__main__":
|
| 30 |
+
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
|
models/schemas.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic import BaseModel
|
| 2 |
+
from typing import List, Optional
|
| 3 |
+
|
| 4 |
+
class KeywordData(BaseModel):
|
| 5 |
+
keyword: str
|
| 6 |
+
search_volume: str
|
| 7 |
+
competition: str
|
| 8 |
+
relevance: int
|
| 9 |
+
|
| 10 |
+
class HashtagData(BaseModel):
|
| 11 |
+
tag: str
|
| 12 |
+
post_count: str
|
| 13 |
+
|
| 14 |
+
class SEORequest(BaseModel):
|
| 15 |
+
content: str
|
| 16 |
+
|
| 17 |
+
class SEOResponse(BaseModel):
|
| 18 |
+
core_keywords: List[KeywordData]
|
| 19 |
+
related_phrases: List[str]
|
| 20 |
+
viral_hashtags: List[HashtagData]
|
| 21 |
+
strategy_tips: List[str]
|
| 22 |
+
target_audience: str
|
| 23 |
+
error: Optional[str] = None
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn
|
| 3 |
+
google-generativeai
|
| 4 |
+
python-dotenv
|
| 5 |
+
pydantic
|
services/analyzer.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import json
|
| 3 |
+
import google.generativeai as genai
|
| 4 |
+
from dotenv import load_dotenv
|
| 5 |
+
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
|
| 8 |
+
# Load .env from parent directory
|
| 9 |
+
env_path = Path(__file__).resolve().parent.parent.parent / ".env"
|
| 10 |
+
load_dotenv(dotenv_path=env_path)
|
| 11 |
+
|
| 12 |
+
API_KEY = os.getenv("GEMINI_API_KEY")
|
| 13 |
+
|
| 14 |
+
if API_KEY:
|
| 15 |
+
genai.configure(api_key=API_KEY)
|
| 16 |
+
|
| 17 |
+
MODELS_FALLBACK = [
|
| 18 |
+
'gemini-2.0-flash',
|
| 19 |
+
'gemini-flash-latest',
|
| 20 |
+
'gemini-pro-latest'
|
| 21 |
+
]
|
| 22 |
+
|
| 23 |
+
def handle_api_error(e, model_name):
|
| 24 |
+
print(f"❌ Error with {model_name}: {e}")
|
| 25 |
+
|
| 26 |
+
def analyze_seo_content(content: str) -> dict:
|
| 27 |
+
"""
|
| 28 |
+
Analyzes content to generate SEO keywords, hashtags, and strategy.
|
| 29 |
+
Returns a dictionary matching the SEOResponse schema.
|
| 30 |
+
"""
|
| 31 |
+
if not API_KEY:
|
| 32 |
+
return {"error": f"GEMINI_API_KEY not found. Looked in: {env_path}, File exists: {env_path.exists()}"}
|
| 33 |
+
|
| 34 |
+
prompt = f"""
|
| 35 |
+
You are a World-Class SEO Strategist & Data Scientist.
|
| 36 |
+
Analyze the following content/topic and generate a professional Keyword Strategy Report.
|
| 37 |
+
|
| 38 |
+
Topic/Content: "{content[:2000]}"
|
| 39 |
+
|
| 40 |
+
Output strictly valid JSON with this structure:
|
| 41 |
+
{{
|
| 42 |
+
"core_keywords": [
|
| 43 |
+
{{"keyword": "example 1", "search_volume": "High", "competition": "Medium", "relevance": 95}},
|
| 44 |
+
{{"keyword": "example 2", "search_volume": "Very High", "competition": "High", "relevance": 90}}
|
| 45 |
+
],
|
| 46 |
+
"related_phrases": [
|
| 47 |
+
"phrase 1", "phrase 2", "phrase 3", "phrase 4", "phrase 5"
|
| 48 |
+
],
|
| 49 |
+
"viral_hashtags": [
|
| 50 |
+
{{"tag": "#Example", "post_count": "1M+"}},
|
| 51 |
+
{{"tag": "#NicheTag", "post_count": "50K+"}}
|
| 52 |
+
],
|
| 53 |
+
"strategy_tips": [
|
| 54 |
+
"Tip 1...", "Tip 2..."
|
| 55 |
+
],
|
| 56 |
+
"target_audience": "Describe the ideal audience for this content."
|
| 57 |
+
}}
|
| 58 |
+
|
| 59 |
+
CRITICAL:
|
| 60 |
+
- METRICS: Estimate 'search_volume' (Low/Medium/High/Very High) and 'competition' based on industry knowledge.
|
| 61 |
+
- RELEVANCE: 0-100 score.
|
| 62 |
+
- Do NOT use markdown code blocks. Output RAW JSON only.
|
| 63 |
+
"""
|
| 64 |
+
|
| 65 |
+
last_error = "No models attempted"
|
| 66 |
+
for model_name in MODELS_FALLBACK:
|
| 67 |
+
try:
|
| 68 |
+
model = genai.GenerativeModel(model_name)
|
| 69 |
+
response = model.generate_content(prompt)
|
| 70 |
+
|
| 71 |
+
if response and response.text:
|
| 72 |
+
text = response.text.replace("```json", "").replace("```", "").strip()
|
| 73 |
+
try:
|
| 74 |
+
data = json.loads(text)
|
| 75 |
+
return data
|
| 76 |
+
except json.JSONDecodeError:
|
| 77 |
+
last_error = "JSON Decode Error"
|
| 78 |
+
continue # Try next model if JSON is broken
|
| 79 |
+
except Exception as e:
|
| 80 |
+
handle_api_error(e, model_name)
|
| 81 |
+
last_error = str(e)
|
| 82 |
+
continue
|
| 83 |
+
|
| 84 |
+
return {"error": f"Analysis failed. Last error: {last_error}"}
|