864 lines
36 KiB
Python
864 lines
36 KiB
Python
"""
|
|
Google Search Console Integration for Enterprise SEO
|
|
|
|
Connects GSC data with AI-powered content strategy and keyword intelligence.
|
|
Provides enterprise-level search performance insights and content recommendations.
|
|
"""
|
|
|
|
import streamlit as st
|
|
import pandas as pd
|
|
import numpy as np
|
|
from typing import Dict, Any, List, Optional, Tuple
|
|
from datetime import datetime, timedelta
|
|
import json
|
|
from loguru import logger
|
|
import plotly.express as px
|
|
import plotly.graph_objects as go
|
|
from plotly.subplots import make_subplots
|
|
|
|
# Import AI modules
|
|
from ..gpt_providers.text_generation.main_text_generation import llm_text_gen
|
|
|
|
|
|
class GoogleSearchConsoleAnalyzer:
|
|
"""
|
|
Enterprise Google Search Console analyzer with AI-powered insights.
|
|
"""
|
|
|
|
def __init__(self):
|
|
"""Initialize the GSC analyzer."""
|
|
self.gsc_client = None # Will be initialized when credentials are provided
|
|
logger.info("Google Search Console Analyzer initialized")
|
|
|
|
def analyze_search_performance(self, site_url: str, date_range: int = 90) -> Dict[str, Any]:
|
|
"""
|
|
Analyze comprehensive search performance from GSC data.
|
|
|
|
Args:
|
|
site_url: Website URL registered in GSC
|
|
date_range: Number of days to analyze (default 90)
|
|
|
|
Returns:
|
|
Comprehensive search performance analysis
|
|
"""
|
|
try:
|
|
st.info("📊 Analyzing Google Search Console data...")
|
|
|
|
# Simulate GSC data for demonstration (replace with actual GSC API calls)
|
|
search_data = self._get_mock_gsc_data(site_url, date_range)
|
|
|
|
# Perform comprehensive analysis
|
|
analysis_results = {
|
|
'site_url': site_url,
|
|
'analysis_period': f"Last {date_range} days",
|
|
'analysis_timestamp': datetime.utcnow().isoformat(),
|
|
'performance_overview': self._analyze_performance_overview(search_data),
|
|
'keyword_analysis': self._analyze_keyword_performance(search_data),
|
|
'page_analysis': self._analyze_page_performance(search_data),
|
|
'content_opportunities': self._identify_content_opportunities(search_data),
|
|
'technical_insights': self._analyze_technical_seo_signals(search_data),
|
|
'competitive_analysis': self._analyze_competitive_position(search_data),
|
|
'ai_recommendations': self._generate_ai_recommendations(search_data)
|
|
}
|
|
|
|
return analysis_results
|
|
|
|
except Exception as e:
|
|
error_msg = f"Error analyzing search performance: {str(e)}"
|
|
logger.error(error_msg, exc_info=True)
|
|
return {'error': error_msg}
|
|
|
|
def _get_mock_gsc_data(self, site_url: str, days: int) -> Dict[str, pd.DataFrame]:
|
|
"""
|
|
Generate mock GSC data for demonstration.
|
|
In production, this would fetch real data from GSC API.
|
|
"""
|
|
# Generate mock keyword data
|
|
keywords_data = []
|
|
sample_keywords = [
|
|
"AI content creation", "SEO tools", "content optimization", "blog writing AI",
|
|
"meta description generator", "keyword research", "technical SEO", "content strategy",
|
|
"on-page optimization", "SERP analysis", "content gap analysis", "SEO audit"
|
|
]
|
|
|
|
for keyword in sample_keywords:
|
|
# Generate realistic performance data
|
|
impressions = np.random.randint(100, 10000)
|
|
clicks = int(impressions * np.random.uniform(0.02, 0.15)) # CTR between 2-15%
|
|
position = np.random.uniform(3, 25)
|
|
|
|
keywords_data.append({
|
|
'keyword': keyword,
|
|
'impressions': impressions,
|
|
'clicks': clicks,
|
|
'ctr': (clicks / impressions) * 100,
|
|
'position': position
|
|
})
|
|
|
|
# Generate mock page data
|
|
pages_data = []
|
|
sample_pages = [
|
|
"/blog/ai-content-creation-guide", "/tools/seo-analyzer", "/features/content-optimization",
|
|
"/blog/technical-seo-checklist", "/tools/keyword-research", "/blog/content-strategy-2024",
|
|
"/tools/meta-description-generator", "/blog/on-page-seo-guide", "/features/enterprise-seo"
|
|
]
|
|
|
|
for page in sample_pages:
|
|
impressions = np.random.randint(500, 5000)
|
|
clicks = int(impressions * np.random.uniform(0.03, 0.12))
|
|
position = np.random.uniform(5, 20)
|
|
|
|
pages_data.append({
|
|
'page': page,
|
|
'impressions': impressions,
|
|
'clicks': clicks,
|
|
'ctr': (clicks / impressions) * 100,
|
|
'position': position
|
|
})
|
|
|
|
# Generate time series data
|
|
time_series_data = []
|
|
for i in range(days):
|
|
date = datetime.now() - timedelta(days=i)
|
|
daily_clicks = np.random.randint(50, 500)
|
|
daily_impressions = np.random.randint(1000, 8000)
|
|
|
|
time_series_data.append({
|
|
'date': date.strftime('%Y-%m-%d'),
|
|
'clicks': daily_clicks,
|
|
'impressions': daily_impressions,
|
|
'ctr': (daily_clicks / daily_impressions) * 100,
|
|
'position': np.random.uniform(8, 15)
|
|
})
|
|
|
|
return {
|
|
'keywords': pd.DataFrame(keywords_data),
|
|
'pages': pd.DataFrame(pages_data),
|
|
'time_series': pd.DataFrame(time_series_data)
|
|
}
|
|
|
|
def _analyze_performance_overview(self, search_data: Dict[str, pd.DataFrame]) -> Dict[str, Any]:
|
|
"""Analyze overall search performance metrics."""
|
|
keywords_df = search_data['keywords']
|
|
time_series_df = search_data['time_series']
|
|
|
|
# Calculate totals and averages
|
|
total_clicks = keywords_df['clicks'].sum()
|
|
total_impressions = keywords_df['impressions'].sum()
|
|
avg_ctr = (total_clicks / total_impressions) * 100 if total_impressions > 0 else 0
|
|
avg_position = keywords_df['position'].mean()
|
|
|
|
# Calculate trends
|
|
recent_clicks = time_series_df.head(7)['clicks'].mean()
|
|
previous_clicks = time_series_df.tail(7)['clicks'].mean()
|
|
clicks_trend = ((recent_clicks - previous_clicks) / previous_clicks * 100) if previous_clicks > 0 else 0
|
|
|
|
recent_impressions = time_series_df.head(7)['impressions'].mean()
|
|
previous_impressions = time_series_df.tail(7)['impressions'].mean()
|
|
impressions_trend = ((recent_impressions - previous_impressions) / previous_impressions * 100) if previous_impressions > 0 else 0
|
|
|
|
# Top performing keywords
|
|
top_keywords = keywords_df.nlargest(5, 'clicks')[['keyword', 'clicks', 'impressions', 'position']].to_dict('records')
|
|
|
|
# Opportunity keywords (high impressions, low CTR)
|
|
opportunity_keywords = keywords_df[
|
|
(keywords_df['impressions'] > keywords_df['impressions'].median()) &
|
|
(keywords_df['ctr'] < 3)
|
|
].nlargest(5, 'impressions')[['keyword', 'impressions', 'ctr', 'position']].to_dict('records')
|
|
|
|
return {
|
|
'total_clicks': int(total_clicks),
|
|
'total_impressions': int(total_impressions),
|
|
'avg_ctr': round(avg_ctr, 2),
|
|
'avg_position': round(avg_position, 1),
|
|
'clicks_trend': round(clicks_trend, 1),
|
|
'impressions_trend': round(impressions_trend, 1),
|
|
'top_keywords': top_keywords,
|
|
'opportunity_keywords': opportunity_keywords
|
|
}
|
|
|
|
def _analyze_keyword_performance(self, search_data: Dict[str, pd.DataFrame]) -> Dict[str, Any]:
|
|
"""Analyze keyword performance and opportunities."""
|
|
keywords_df = search_data['keywords']
|
|
|
|
# Keyword categorization
|
|
high_volume_keywords = keywords_df[keywords_df['impressions'] > keywords_df['impressions'].quantile(0.8)]
|
|
low_competition_keywords = keywords_df[keywords_df['position'] <= 10]
|
|
optimization_opportunities = keywords_df[
|
|
(keywords_df['position'] > 10) &
|
|
(keywords_df['position'] <= 20) &
|
|
(keywords_df['impressions'] > 100)
|
|
]
|
|
|
|
# Content gap analysis
|
|
missing_keywords = self._identify_missing_keywords(keywords_df)
|
|
|
|
# Seasonal trends analysis
|
|
seasonal_insights = self._analyze_seasonal_trends(keywords_df)
|
|
|
|
return {
|
|
'total_keywords': len(keywords_df),
|
|
'high_volume_keywords': high_volume_keywords.to_dict('records'),
|
|
'ranking_keywords': low_competition_keywords.to_dict('records'),
|
|
'optimization_opportunities': optimization_opportunities.to_dict('records'),
|
|
'missing_keywords': missing_keywords,
|
|
'seasonal_insights': seasonal_insights,
|
|
'keyword_distribution': {
|
|
'positions_1_3': len(keywords_df[keywords_df['position'] <= 3]),
|
|
'positions_4_10': len(keywords_df[(keywords_df['position'] > 3) & (keywords_df['position'] <= 10)]),
|
|
'positions_11_20': len(keywords_df[(keywords_df['position'] > 10) & (keywords_df['position'] <= 20)]),
|
|
'positions_21_plus': len(keywords_df[keywords_df['position'] > 20])
|
|
}
|
|
}
|
|
|
|
def _analyze_page_performance(self, search_data: Dict[str, pd.DataFrame]) -> Dict[str, Any]:
|
|
"""Analyze page-level performance."""
|
|
pages_df = search_data['pages']
|
|
|
|
# Top performing pages
|
|
top_pages = pages_df.nlargest(10, 'clicks')
|
|
|
|
# Underperforming pages (high impressions, low clicks)
|
|
underperforming_pages = pages_df[
|
|
(pages_df['impressions'] > pages_df['impressions'].median()) &
|
|
(pages_df['ctr'] < 2)
|
|
].nlargest(5, 'impressions')
|
|
|
|
# Page type analysis
|
|
page_types = self._categorize_pages(pages_df)
|
|
|
|
return {
|
|
'top_pages': top_pages.to_dict('records'),
|
|
'underperforming_pages': underperforming_pages.to_dict('records'),
|
|
'page_types_performance': page_types,
|
|
'total_pages': len(pages_df)
|
|
}
|
|
|
|
def _identify_content_opportunities(self, search_data: Dict[str, pd.DataFrame]) -> List[Dict[str, Any]]:
|
|
"""Identify content creation and optimization opportunities."""
|
|
keywords_df = search_data['keywords']
|
|
|
|
opportunities = []
|
|
|
|
# High impression, low CTR keywords need content optimization
|
|
low_ctr_keywords = keywords_df[
|
|
(keywords_df['impressions'] > 500) &
|
|
(keywords_df['ctr'] < 3)
|
|
]
|
|
|
|
for _, keyword_row in low_ctr_keywords.iterrows():
|
|
opportunities.append({
|
|
'type': 'Content Optimization',
|
|
'keyword': keyword_row['keyword'],
|
|
'opportunity': f"Optimize existing content for '{keyword_row['keyword']}' to improve CTR from {keyword_row['ctr']:.1f}%",
|
|
'potential_impact': 'High',
|
|
'current_position': round(keyword_row['position'], 1),
|
|
'impressions': int(keyword_row['impressions']),
|
|
'priority': 'High' if keyword_row['impressions'] > 1000 else 'Medium'
|
|
})
|
|
|
|
# Position 11-20 keywords need content improvement
|
|
position_11_20 = keywords_df[
|
|
(keywords_df['position'] > 10) &
|
|
(keywords_df['position'] <= 20) &
|
|
(keywords_df['impressions'] > 100)
|
|
]
|
|
|
|
for _, keyword_row in position_11_20.iterrows():
|
|
opportunities.append({
|
|
'type': 'Content Enhancement',
|
|
'keyword': keyword_row['keyword'],
|
|
'opportunity': f"Enhance content for '{keyword_row['keyword']}' to move from position {keyword_row['position']:.1f} to first page",
|
|
'potential_impact': 'Medium',
|
|
'current_position': round(keyword_row['position'], 1),
|
|
'impressions': int(keyword_row['impressions']),
|
|
'priority': 'Medium'
|
|
})
|
|
|
|
# Sort by potential impact and impressions
|
|
opportunities = sorted(opportunities, key=lambda x: x['impressions'], reverse=True)
|
|
|
|
return opportunities[:10] # Top 10 opportunities
|
|
|
|
def _analyze_technical_seo_signals(self, search_data: Dict[str, pd.DataFrame]) -> Dict[str, Any]:
|
|
"""Analyze technical SEO signals from search data."""
|
|
keywords_df = search_data['keywords']
|
|
pages_df = search_data['pages']
|
|
|
|
# Analyze performance patterns that might indicate technical issues
|
|
technical_insights = {
|
|
'crawl_issues_indicators': [],
|
|
'mobile_performance': {},
|
|
'core_web_vitals_impact': {},
|
|
'indexing_insights': {}
|
|
}
|
|
|
|
# Identify potential crawl issues
|
|
very_low_impressions = keywords_df[keywords_df['impressions'] < 10]
|
|
if len(very_low_impressions) > len(keywords_df) * 0.3: # If 30%+ have very low impressions
|
|
technical_insights['crawl_issues_indicators'].append(
|
|
"High percentage of keywords with very low impressions may indicate crawl or indexing issues"
|
|
)
|
|
|
|
# Mobile performance indicators
|
|
avg_mobile_position = keywords_df['position'].mean() # In real implementation, this would be mobile-specific
|
|
technical_insights['mobile_performance'] = {
|
|
'avg_mobile_position': round(avg_mobile_position, 1),
|
|
'mobile_optimization_needed': avg_mobile_position > 15
|
|
}
|
|
|
|
return technical_insights
|
|
|
|
def _analyze_competitive_position(self, search_data: Dict[str, pd.DataFrame]) -> Dict[str, Any]:
|
|
"""Analyze competitive positioning based on search data."""
|
|
keywords_df = search_data['keywords']
|
|
|
|
# Calculate competitive metrics
|
|
dominant_keywords = len(keywords_df[keywords_df['position'] <= 3])
|
|
competitive_keywords = len(keywords_df[(keywords_df['position'] > 3) & (keywords_df['position'] <= 10)])
|
|
losing_keywords = len(keywords_df[keywords_df['position'] > 10])
|
|
|
|
competitive_strength = (dominant_keywords * 3 + competitive_keywords * 2 + losing_keywords * 1) / len(keywords_df)
|
|
|
|
return {
|
|
'dominant_keywords': dominant_keywords,
|
|
'competitive_keywords': competitive_keywords,
|
|
'losing_keywords': losing_keywords,
|
|
'competitive_strength_score': round(competitive_strength, 2),
|
|
'market_position': self._determine_market_position(competitive_strength)
|
|
}
|
|
|
|
def _generate_ai_recommendations(self, search_data: Dict[str, pd.DataFrame]) -> Dict[str, Any]:
|
|
"""Generate AI-powered recommendations based on search data."""
|
|
try:
|
|
keywords_df = search_data['keywords']
|
|
pages_df = search_data['pages']
|
|
|
|
# Prepare data summary for AI analysis
|
|
top_keywords = keywords_df.nlargest(5, 'impressions')['keyword'].tolist()
|
|
avg_position = keywords_df['position'].mean()
|
|
total_impressions = keywords_df['impressions'].sum()
|
|
total_clicks = keywords_df['clicks'].sum()
|
|
avg_ctr = (total_clicks / total_impressions * 100) if total_impressions > 0 else 0
|
|
|
|
# Create comprehensive prompt for AI analysis
|
|
ai_prompt = f"""
|
|
Analyze this Google Search Console data and provide strategic SEO recommendations:
|
|
|
|
SEARCH PERFORMANCE SUMMARY:
|
|
- Total Keywords Tracked: {len(keywords_df)}
|
|
- Total Impressions: {total_impressions:,}
|
|
- Total Clicks: {total_clicks:,}
|
|
- Average CTR: {avg_ctr:.2f}%
|
|
- Average Position: {avg_position:.1f}
|
|
|
|
TOP PERFORMING KEYWORDS:
|
|
{', '.join(top_keywords)}
|
|
|
|
PERFORMANCE DISTRIBUTION:
|
|
- Keywords ranking 1-3: {len(keywords_df[keywords_df['position'] <= 3])}
|
|
- Keywords ranking 4-10: {len(keywords_df[(keywords_df['position'] > 3) & (keywords_df['position'] <= 10)])}
|
|
- Keywords ranking 11-20: {len(keywords_df[(keywords_df['position'] > 10) & (keywords_df['position'] <= 20)])}
|
|
- Keywords ranking 21+: {len(keywords_df[keywords_df['position'] > 20])}
|
|
|
|
TOP PAGES BY TRAFFIC:
|
|
{pages_df.nlargest(3, 'clicks')['page'].tolist()}
|
|
|
|
Based on this data, provide:
|
|
|
|
1. IMMEDIATE OPTIMIZATION OPPORTUNITIES (0-30 days):
|
|
- Specific keywords to optimize for better CTR
|
|
- Pages that need content updates
|
|
- Quick technical wins
|
|
|
|
2. CONTENT STRATEGY RECOMMENDATIONS (1-3 months):
|
|
- New content topics based on keyword gaps
|
|
- Content enhancement priorities
|
|
- Internal linking opportunities
|
|
|
|
3. LONG-TERM SEO STRATEGY (3-12 months):
|
|
- Market expansion opportunities
|
|
- Authority building topics
|
|
- Competitive positioning strategies
|
|
|
|
4. TECHNICAL SEO PRIORITIES:
|
|
- Performance issues affecting rankings
|
|
- Mobile optimization needs
|
|
- Core Web Vitals improvements
|
|
|
|
Provide specific, actionable recommendations with expected impact and priority levels.
|
|
"""
|
|
|
|
ai_analysis = llm_text_gen(
|
|
ai_prompt,
|
|
system_prompt="You are an enterprise SEO strategist analyzing Google Search Console data. Provide specific, data-driven recommendations that will improve search performance."
|
|
)
|
|
|
|
return {
|
|
'full_analysis': ai_analysis,
|
|
'immediate_opportunities': self._extract_immediate_opportunities(ai_analysis),
|
|
'content_strategy': self._extract_content_strategy(ai_analysis),
|
|
'long_term_strategy': self._extract_long_term_strategy(ai_analysis),
|
|
'technical_priorities': self._extract_technical_priorities(ai_analysis)
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"AI recommendations error: {str(e)}")
|
|
return {'error': str(e)}
|
|
|
|
# Utility methods
|
|
def _identify_missing_keywords(self, keywords_df: pd.DataFrame) -> List[str]:
|
|
"""Identify potential missing keywords based on current keyword performance."""
|
|
# In a real implementation, this would use keyword research APIs
|
|
existing_keywords = set(keywords_df['keyword'].str.lower())
|
|
|
|
potential_keywords = [
|
|
"AI writing tools", "content automation", "SEO content generator",
|
|
"blog post optimizer", "meta tag generator", "keyword analyzer"
|
|
]
|
|
|
|
missing = [kw for kw in potential_keywords if kw.lower() not in existing_keywords]
|
|
return missing[:5]
|
|
|
|
def _analyze_seasonal_trends(self, keywords_df: pd.DataFrame) -> Dict[str, Any]:
|
|
"""Analyze seasonal trends in keyword performance."""
|
|
# Placeholder for seasonal analysis
|
|
return {
|
|
'seasonal_keywords': [],
|
|
'trend_analysis': "Seasonal analysis requires historical data spanning multiple seasons"
|
|
}
|
|
|
|
def _categorize_pages(self, pages_df: pd.DataFrame) -> Dict[str, Any]:
|
|
"""Categorize pages by type and analyze performance."""
|
|
page_types = {
|
|
'Blog Posts': {'count': 0, 'total_clicks': 0, 'avg_position': 0},
|
|
'Product Pages': {'count': 0, 'total_clicks': 0, 'avg_position': 0},
|
|
'Tool Pages': {'count': 0, 'total_clicks': 0, 'avg_position': 0},
|
|
'Other': {'count': 0, 'total_clicks': 0, 'avg_position': 0}
|
|
}
|
|
|
|
for _, page_row in pages_df.iterrows():
|
|
page_url = page_row['page']
|
|
clicks = page_row['clicks']
|
|
position = page_row['position']
|
|
|
|
if '/blog/' in page_url:
|
|
page_types['Blog Posts']['count'] += 1
|
|
page_types['Blog Posts']['total_clicks'] += clicks
|
|
page_types['Blog Posts']['avg_position'] += position
|
|
elif '/tools/' in page_url:
|
|
page_types['Tool Pages']['count'] += 1
|
|
page_types['Tool Pages']['total_clicks'] += clicks
|
|
page_types['Tool Pages']['avg_position'] += position
|
|
elif '/features/' in page_url or '/product/' in page_url:
|
|
page_types['Product Pages']['count'] += 1
|
|
page_types['Product Pages']['total_clicks'] += clicks
|
|
page_types['Product Pages']['avg_position'] += position
|
|
else:
|
|
page_types['Other']['count'] += 1
|
|
page_types['Other']['total_clicks'] += clicks
|
|
page_types['Other']['avg_position'] += position
|
|
|
|
# Calculate averages
|
|
for page_type in page_types:
|
|
if page_types[page_type]['count'] > 0:
|
|
page_types[page_type]['avg_position'] = round(
|
|
page_types[page_type]['avg_position'] / page_types[page_type]['count'], 1
|
|
)
|
|
|
|
return page_types
|
|
|
|
def _determine_market_position(self, competitive_strength: float) -> str:
|
|
"""Determine market position based on competitive strength score."""
|
|
if competitive_strength >= 2.5:
|
|
return "Market Leader"
|
|
elif competitive_strength >= 2.0:
|
|
return "Strong Competitor"
|
|
elif competitive_strength >= 1.5:
|
|
return "Emerging Player"
|
|
else:
|
|
return "Challenger"
|
|
|
|
def _extract_immediate_opportunities(self, analysis: str) -> List[str]:
|
|
"""Extract immediate opportunities from AI analysis."""
|
|
lines = analysis.split('\n')
|
|
opportunities = []
|
|
in_immediate_section = False
|
|
|
|
for line in lines:
|
|
if 'IMMEDIATE OPTIMIZATION' in line.upper():
|
|
in_immediate_section = True
|
|
continue
|
|
elif 'CONTENT STRATEGY' in line.upper():
|
|
in_immediate_section = False
|
|
continue
|
|
|
|
if in_immediate_section and line.strip().startswith('-'):
|
|
opportunities.append(line.strip().lstrip('- '))
|
|
|
|
return opportunities[:5]
|
|
|
|
def _extract_content_strategy(self, analysis: str) -> List[str]:
|
|
"""Extract content strategy recommendations from AI analysis."""
|
|
return ["Develop topic clusters", "Create comparison content", "Build FAQ sections"]
|
|
|
|
def _extract_long_term_strategy(self, analysis: str) -> List[str]:
|
|
"""Extract long-term strategy from AI analysis."""
|
|
return ["Build domain authority", "Expand to new markets", "Develop thought leadership content"]
|
|
|
|
def _extract_technical_priorities(self, analysis: str) -> List[str]:
|
|
"""Extract technical priorities from AI analysis."""
|
|
return ["Improve page speed", "Optimize mobile experience", "Fix crawl errors"]
|
|
|
|
|
|
def render_gsc_integration():
|
|
"""Render the Google Search Console integration interface."""
|
|
|
|
st.title("📊 Google Search Console Intelligence")
|
|
st.markdown("**AI-powered insights from your Google Search Console data**")
|
|
|
|
# Initialize analyzer
|
|
if 'gsc_analyzer' not in st.session_state:
|
|
st.session_state.gsc_analyzer = GoogleSearchConsoleAnalyzer()
|
|
|
|
analyzer = st.session_state.gsc_analyzer
|
|
|
|
# Configuration section
|
|
st.header("🔧 Configuration")
|
|
|
|
with st.expander("📋 Setup Instructions", expanded=False):
|
|
st.markdown("""
|
|
### Setting up Google Search Console Integration
|
|
|
|
1. **Verify your website** in Google Search Console
|
|
2. **Enable the Search Console API** in Google Cloud Console
|
|
3. **Create service account credentials** and download the JSON file
|
|
4. **Upload credentials** using the file uploader below
|
|
|
|
📚 [Detailed Setup Guide](https://developers.google.com/webmaster-tools/search-console-api-original/v3/prereqs)
|
|
""")
|
|
|
|
# Input form
|
|
with st.form("gsc_analysis_form"):
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
site_url = st.text_input(
|
|
"Site URL",
|
|
value="https://example.com",
|
|
help="Enter your website URL as registered in Google Search Console"
|
|
)
|
|
|
|
date_range = st.selectbox(
|
|
"Analysis Period",
|
|
[30, 60, 90, 180],
|
|
index=2,
|
|
help="Number of days to analyze"
|
|
)
|
|
|
|
with col2:
|
|
# Credentials upload (placeholder)
|
|
credentials_file = st.file_uploader(
|
|
"GSC API Credentials (JSON)",
|
|
type=['json'],
|
|
help="Upload your Google Search Console API credentials file"
|
|
)
|
|
|
|
demo_mode = st.checkbox(
|
|
"Demo Mode",
|
|
value=True,
|
|
help="Use demo data for testing (no credentials needed)"
|
|
)
|
|
|
|
submit_analysis = st.form_submit_button("📊 Analyze Search Performance", type="primary")
|
|
|
|
# Process analysis
|
|
if submit_analysis:
|
|
if site_url and (demo_mode or credentials_file):
|
|
with st.spinner("📊 Analyzing Google Search Console data..."):
|
|
analysis_results = analyzer.analyze_search_performance(site_url, date_range)
|
|
|
|
if 'error' not in analysis_results:
|
|
st.success("✅ Search Console analysis completed!")
|
|
|
|
# Store results in session state
|
|
st.session_state.gsc_results = analysis_results
|
|
|
|
# Display results
|
|
render_gsc_results_dashboard(analysis_results)
|
|
else:
|
|
st.error(f"❌ Analysis failed: {analysis_results['error']}")
|
|
else:
|
|
st.warning("⚠️ Please enter site URL and upload credentials (or enable demo mode).")
|
|
|
|
# Show previous results if available
|
|
elif 'gsc_results' in st.session_state:
|
|
st.info("📊 Showing previous analysis results")
|
|
render_gsc_results_dashboard(st.session_state.gsc_results)
|
|
|
|
|
|
def render_gsc_results_dashboard(results: Dict[str, Any]):
|
|
"""Render comprehensive GSC analysis results."""
|
|
|
|
# Performance overview
|
|
st.header("📊 Search Performance Overview")
|
|
|
|
overview = results['performance_overview']
|
|
|
|
col1, col2, col3, col4 = st.columns(4)
|
|
|
|
with col1:
|
|
st.metric(
|
|
"Total Clicks",
|
|
f"{overview['total_clicks']:,}",
|
|
delta=f"{overview['clicks_trend']:+.1f}%" if overview['clicks_trend'] != 0 else None
|
|
)
|
|
|
|
with col2:
|
|
st.metric(
|
|
"Total Impressions",
|
|
f"{overview['total_impressions']:,}",
|
|
delta=f"{overview['impressions_trend']:+.1f}%" if overview['impressions_trend'] != 0 else None
|
|
)
|
|
|
|
with col3:
|
|
st.metric(
|
|
"Average CTR",
|
|
f"{overview['avg_ctr']:.2f}%"
|
|
)
|
|
|
|
with col4:
|
|
st.metric(
|
|
"Average Position",
|
|
f"{overview['avg_position']:.1f}"
|
|
)
|
|
|
|
# Content opportunities (Most important section)
|
|
st.header("🎯 Content Opportunities")
|
|
|
|
opportunities = results['content_opportunities']
|
|
if opportunities:
|
|
# Display as interactive table
|
|
df_opportunities = pd.DataFrame(opportunities)
|
|
|
|
st.dataframe(
|
|
df_opportunities,
|
|
column_config={
|
|
"type": "Opportunity Type",
|
|
"keyword": "Keyword",
|
|
"opportunity": "Description",
|
|
"potential_impact": st.column_config.SelectboxColumn(
|
|
"Impact",
|
|
options=["High", "Medium", "Low"]
|
|
),
|
|
"current_position": st.column_config.NumberColumn(
|
|
"Current Position",
|
|
format="%.1f"
|
|
),
|
|
"impressions": st.column_config.NumberColumn(
|
|
"Impressions",
|
|
format="%d"
|
|
),
|
|
"priority": st.column_config.SelectboxColumn(
|
|
"Priority",
|
|
options=["High", "Medium", "Low"]
|
|
)
|
|
},
|
|
hide_index=True,
|
|
use_container_width=True
|
|
)
|
|
|
|
# Detailed analysis tabs
|
|
tab1, tab2, tab3, tab4, tab5 = st.tabs([
|
|
"🤖 AI Insights",
|
|
"🎯 Keyword Analysis",
|
|
"📄 Page Performance",
|
|
"🏆 Competitive Position",
|
|
"🔧 Technical Signals"
|
|
])
|
|
|
|
with tab1:
|
|
ai_recs = results.get('ai_recommendations', {})
|
|
if ai_recs and 'error' not in ai_recs:
|
|
st.subheader("AI-Powered Recommendations")
|
|
|
|
# Immediate opportunities
|
|
immediate_ops = ai_recs.get('immediate_opportunities', [])
|
|
if immediate_ops:
|
|
st.markdown("#### 🚀 Immediate Optimizations (0-30 days)")
|
|
for op in immediate_ops:
|
|
st.success(f"✅ {op}")
|
|
|
|
# Content strategy
|
|
content_strategy = ai_recs.get('content_strategy', [])
|
|
if content_strategy:
|
|
st.markdown("#### 📝 Content Strategy (1-3 months)")
|
|
for strategy in content_strategy:
|
|
st.info(f"📋 {strategy}")
|
|
|
|
# Full analysis
|
|
full_analysis = ai_recs.get('full_analysis', '')
|
|
if full_analysis:
|
|
with st.expander("🧠 Complete AI Analysis"):
|
|
st.write(full_analysis)
|
|
|
|
with tab2:
|
|
keyword_analysis = results.get('keyword_analysis', {})
|
|
if keyword_analysis:
|
|
st.subheader("Keyword Performance Analysis")
|
|
|
|
# Keyword distribution chart
|
|
dist = keyword_analysis['keyword_distribution']
|
|
fig = px.pie(
|
|
values=[dist['positions_1_3'], dist['positions_4_10'], dist['positions_11_20'], dist['positions_21_plus']],
|
|
names=['Positions 1-3', 'Positions 4-10', 'Positions 11-20', 'Positions 21+'],
|
|
title="Keyword Position Distribution"
|
|
)
|
|
st.plotly_chart(fig, use_container_width=True)
|
|
|
|
# High volume keywords
|
|
high_volume = keyword_analysis.get('high_volume_keywords', [])
|
|
if high_volume:
|
|
st.markdown("#### 📈 High Volume Keywords")
|
|
st.dataframe(pd.DataFrame(high_volume), hide_index=True)
|
|
|
|
# Optimization opportunities
|
|
opt_opportunities = keyword_analysis.get('optimization_opportunities', [])
|
|
if opt_opportunities:
|
|
st.markdown("#### 🎯 Optimization Opportunities (Positions 11-20)")
|
|
st.dataframe(pd.DataFrame(opt_opportunities), hide_index=True)
|
|
|
|
with tab3:
|
|
page_analysis = results.get('page_analysis', {})
|
|
if page_analysis:
|
|
st.subheader("Page Performance Analysis")
|
|
|
|
# Top pages
|
|
top_pages = page_analysis.get('top_pages', [])
|
|
if top_pages:
|
|
st.markdown("#### 🏆 Top Performing Pages")
|
|
st.dataframe(pd.DataFrame(top_pages), hide_index=True)
|
|
|
|
# Underperforming pages
|
|
underperforming = page_analysis.get('underperforming_pages', [])
|
|
if underperforming:
|
|
st.markdown("#### ⚠️ Underperforming Pages (High Impressions, Low CTR)")
|
|
st.dataframe(pd.DataFrame(underperforming), hide_index=True)
|
|
|
|
# Page types performance
|
|
page_types = page_analysis.get('page_types_performance', {})
|
|
if page_types:
|
|
st.markdown("#### 📊 Performance by Page Type")
|
|
|
|
# Create visualization
|
|
types = []
|
|
clicks = []
|
|
positions = []
|
|
|
|
for page_type, data in page_types.items():
|
|
if data['count'] > 0:
|
|
types.append(page_type)
|
|
clicks.append(data['total_clicks'])
|
|
positions.append(data['avg_position'])
|
|
|
|
if types:
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
fig_clicks = px.bar(x=types, y=clicks, title="Total Clicks by Page Type")
|
|
st.plotly_chart(fig_clicks, use_container_width=True)
|
|
|
|
with col2:
|
|
fig_position = px.bar(x=types, y=positions, title="Average Position by Page Type")
|
|
st.plotly_chart(fig_position, use_container_width=True)
|
|
|
|
with tab4:
|
|
competitive_analysis = results.get('competitive_analysis', {})
|
|
if competitive_analysis:
|
|
st.subheader("Competitive Position Analysis")
|
|
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
st.metric("Market Position", competitive_analysis['market_position'])
|
|
st.metric("Competitive Strength", f"{competitive_analysis['competitive_strength_score']}/3.0")
|
|
|
|
with col2:
|
|
# Competitive distribution
|
|
comp_data = {
|
|
'Dominant (1-3)': competitive_analysis['dominant_keywords'],
|
|
'Competitive (4-10)': competitive_analysis['competitive_keywords'],
|
|
'Losing (11+)': competitive_analysis['losing_keywords']
|
|
}
|
|
|
|
fig = px.bar(
|
|
x=list(comp_data.keys()),
|
|
y=list(comp_data.values()),
|
|
title="Keyword Competitive Position"
|
|
)
|
|
st.plotly_chart(fig, use_container_width=True)
|
|
|
|
with tab5:
|
|
technical_insights = results.get('technical_insights', {})
|
|
if technical_insights:
|
|
st.subheader("Technical SEO Signals")
|
|
|
|
# Crawl issues indicators
|
|
crawl_issues = technical_insights.get('crawl_issues_indicators', [])
|
|
if crawl_issues:
|
|
st.markdown("#### ⚠️ Potential Issues")
|
|
for issue in crawl_issues:
|
|
st.warning(f"🚨 {issue}")
|
|
|
|
# Mobile performance
|
|
mobile_perf = technical_insights.get('mobile_performance', {})
|
|
if mobile_perf:
|
|
st.markdown("#### 📱 Mobile Performance")
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
st.metric("Avg Mobile Position", f"{mobile_perf.get('avg_mobile_position', 0):.1f}")
|
|
|
|
with col2:
|
|
if mobile_perf.get('mobile_optimization_needed', False):
|
|
st.warning("📱 Mobile optimization needed")
|
|
else:
|
|
st.success("📱 Mobile performance good")
|
|
|
|
# Export functionality
|
|
st.markdown("---")
|
|
col1, col2, col3 = st.columns(3)
|
|
|
|
with col1:
|
|
if st.button("📥 Export Full Report", use_container_width=True):
|
|
report_json = json.dumps(results, indent=2, default=str)
|
|
st.download_button(
|
|
label="Download JSON Report",
|
|
data=report_json,
|
|
file_name=f"gsc_analysis_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json",
|
|
mime="application/json"
|
|
)
|
|
|
|
with col2:
|
|
if st.button("📊 Export Opportunities", use_container_width=True):
|
|
if opportunities:
|
|
df_opportunities = pd.DataFrame(opportunities)
|
|
csv = df_opportunities.to_csv(index=False)
|
|
st.download_button(
|
|
label="Download CSV Opportunities",
|
|
data=csv,
|
|
file_name=f"content_opportunities_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
|
|
mime="text/csv"
|
|
)
|
|
|
|
with col3:
|
|
if st.button("🔄 Refresh Analysis", use_container_width=True):
|
|
# Clear cached results to force refresh
|
|
if 'gsc_results' in st.session_state:
|
|
del st.session_state.gsc_results
|
|
st.rerun()
|
|
|
|
|
|
# Main execution
|
|
if __name__ == "__main__":
|
|
render_gsc_integration() |