| import streamlit as st |
| import math |
|
|
| |
| class SEOImpactForecaster: |
| """ |
| A class to calculate the estimated incremental SEO traffic based on internal linking improvements. |
| Follows Single Responsibility Principle (SRP) by focusing solely on calculation. |
| """ |
| _CTR_CURVE = { |
| 1: 0.28, |
| 2: 0.15, |
| 3: 0.10, |
| 4: 0.07, |
| 5: 0.05, |
| 6: 0.03, |
| 7: 0.02, |
| 8: 0.015, |
| 9: 0.01, |
| 10: 0.008 |
| } |
|
|
| def calculate_incremental_traffic( |
| self, |
| current_position: int, |
| target_position: int, |
| search_volume_proxy_monthly: float, |
| manual_ctr_improvement: float = None |
| ) -> dict: |
| """ |
| Calculates the estimated incremental SEO traffic. |
| |
| Args: |
| current_position (int): The current average search engine ranking position for the keywords. |
| target_position (int): The target average search engine ranking position after internal linking. |
| search_volume_proxy_monthly (float): Monthly proxy for search volume (e.g., GSC Impressions / 12). |
| manual_ctr_improvement (float, optional): Manual CTR improvement as a decimal (e.g., 0.10 for 10%). |
| If provided, this overrides the position-based CTR estimation. |
| |
| Returns: |
| dict: A dictionary containing the estimated monthly and annual incremental traffic. |
| """ |
| |
| if search_volume_proxy_monthly < 0: |
| raise ValueError("Monthly search volume proxy cannot be negative.") |
|
|
| |
| if manual_ctr_improvement is not None: |
| if not (0 <= manual_ctr_improvement <= 1): |
| raise ValueError("Manual CTR improvement must be a decimal between 0 and 1 (e.g., 0.10 for 10%).") |
| ctr_improvement = manual_ctr_improvement |
| st.info(f"Using manual CTR improvement: {manual_ctr_improvement:.2%}") |
| else: |
| |
| if not (1 <= current_position <= 10 and 1 <= target_position <= 10): |
| st.warning("Current and target positions should ideally be between 1 and 10 for reasonable CTR estimation.") |
| |
| current_position = max(1, min(10, current_position)) |
| target_position = max(1, min(10, target_position)) |
|
|
| current_ctr = self._CTR_CURVE.get(current_position, 0) |
| target_ctr = self._CTR_CURVE.get(target_position, 0) |
|
|
| ctr_improvement = target_ctr - current_ctr |
|
|
| if ctr_improvement < 0: |
| st.warning(f"Target position ({target_position}) has a lower estimated CTR ({target_ctr:.2%}) than current position ({current_position}) ({current_ctr:.2%}). This will result in negative incremental traffic.") |
| else: |
| st.info(f"Estimated CTR improvement from Pos {current_position} ({current_ctr:.2%}) to Pos {target_position} ({target_ctr:.2%}): {ctr_improvement:.2%} percentage points.") |
|
|
| incremental_traffic_monthly = ctr_improvement * search_volume_proxy_monthly |
| incremental_traffic_annual = incremental_traffic_monthly * 12 |
|
|
| return { |
| "incremental_traffic_monthly": incremental_traffic_monthly, |
| "incremental_traffic_annual": incremental_traffic_annual |
| } |
|
|
| def run_streamlit_app(): |
| """ |
| Main function to run the Streamlit application for SEO Impact Forecaster. |
| Handles user input and displays results using Streamlit widgets. |
| """ |
| st.set_page_config( |
| page_title="Internal Linking SEO Impact Forecaster", |
| layout="centered", |
| initial_sidebar_state="auto" |
| ) |
|
|
| st.title("Internal Linking SEO Impact Forecaster") |
| st.markdown("This tool helps estimate incremental SEO traffic from internal linking improvements.") |
| st.markdown("---") |
|
|
| |
| search_volume_proxy_monthly = st.number_input( |
| "Enter the approximate *monthly total search impressions* for the keywords you are targeting:", |
| min_value=0.0, |
| value=100000.0, |
| step=10000.0, |
| format="%f", |
| help="This can be obtained from Google Search Console (GSC) Impressions data. " |
| "If you have annual impressions, divide by 12 to get a monthly figure. " |
| "For example, if you get 24 million annual impressions, enter 2000000 for monthly." |
| ) |
|
|
| |
| ctr_method = st.radio( |
| "How do you want to determine CTR improvement?", |
| ("Estimate based on position change", "Enter specific CTR improvement percentage") |
| ) |
|
|
| manual_ctr_decimal = None |
| current_pos = None |
| target_pos = None |
|
|
| if ctr_method == "Enter specific CTR improvement percentage": |
| manual_ctr_percent = st.number_input( |
| "Enter the CTR improvement percentage (e.g., 10 for 10%):", |
| min_value=0.0, |
| max_value=100.0, |
| value=5.0, |
| step=0.1, |
| format="%f", |
| help="Enter the expected increase in CTR as a percentage (e.g., 5 for a 5% increase in CTR)." |
| ) |
| manual_ctr_decimal = manual_ctr_percent / 100 |
| else: |
| current_pos = st.number_input( |
| "Enter your current average SEO ranking position (1-10 recommended):", |
| min_value=1, |
| max_value=100, |
| value=5, |
| step=1, |
| help="The current average position of your targeted keywords in search results." |
| ) |
| target_pos = st.number_input( |
| "Enter your target average SEO ranking position (1-10 recommended):", |
| min_value=1, |
| max_value=100, |
| value=3, |
| step=1, |
| help="The desired average position after implementing internal linking strategies." |
| ) |
|
|
| st.markdown("---") |
|
|
| |
| if st.button("Calculate Estimated Traffic"): |
| forecaster = SEOImpactForecaster() |
| try: |
| if manual_ctr_decimal is not None: |
| results = forecaster.calculate_incremental_traffic( |
| current_position=1, |
| target_position=1, |
| search_volume_proxy_monthly=search_volume_proxy_monthly, |
| manual_ctr_improvement=manual_ctr_decimal |
| ) |
| else: |
| results = forecaster.calculate_incremental_traffic( |
| current_position=current_pos, |
| target_position=target_pos, |
| search_volume_proxy_monthly=search_volume_proxy_monthly |
| ) |
|
|
| st.success("### Forecast Results") |
| st.metric( |
| label="Estimated Monthly Incremental Traffic", |
| value=f"{math.ceil(results['incremental_traffic_monthly']):,}" |
| ) |
| st.metric( |
| label="Estimated Annual Incremental Traffic", |
| value=f"{math.ceil(results['incremental_traffic_annual']):,}" |
| ) |
|
|
| except ValueError as e: |
| st.error(f"Error: {e}") |
| except Exception as e: |
| st.error(f"An unexpected error occurred: {e}") |
|
|
| st.markdown("---") |
| st.info("### Important Notes") |
| st.markdown(""" |
| - The CTR curve used is a **simplified model**. Actual CTRs vary greatly by niche, SERP features, and keyword intent. |
| - 'Search Volume Proxy' assumes Google Search Console (GSC) Impressions / 12 is a good stand-in for actual search volume. |
| - This is a **forecast**; actual results may differ based on many other SEO factors (e.g., content quality, competition, technical SEO). |
| - For more accurate CTR improvements, consider using A/B testing or historical data specific to your site. |
| """) |
|
|
| |
| st.markdown("---") |
| st.info("### Reference CTR Benchmarks (Simplified Model)") |
| st.markdown(""" |
| These are the approximate Click-Through Rates (CTRs) used in this model based on general observations. |
| Please note these are simplified and actual CTRs can vary significantly. |
| """) |
| |
| |
| ctr_data = SEOImpactForecaster._CTR_CURVE |
| |
| st.table( |
| {"Position": list(ctr_data.keys()), |
| "Approximate CTR": [f"{v:.2%}" for v in ctr_data.values()]} |
| ) |
|
|
|
|
| |
| |
| if __name__ == "__main__": |
| run_streamlit_app() |