limitedonly41 commited on
Commit
afd7991
Β·
verified Β·
1 Parent(s): 48430c0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +247 -62
app.py CHANGED
@@ -1,123 +1,308 @@
1
  import gradio as gr
2
  import os
3
- from parser import SemRush
4
  import json
 
 
 
 
 
5
 
6
  # Get credentials from Hugging Face Secrets
7
  SEMRUSH_USER_ID = os.getenv("SEMRUSH_USER_ID")
8
  SEMRUSH_API_KEY = os.getenv("SEMRUSH_API_KEY")
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  def get_domain_overview(domain):
11
  """Get domain overview data from SemRush."""
12
  try:
 
 
 
13
  semrush = SemRush(SEMRUSH_USER_ID, SEMRUSH_API_KEY, domain)
14
  overview = semrush.domainOverview()
15
 
16
  if "error" in overview:
17
- return f"Error: {overview['error']}"
18
 
19
- # Format the data for display
20
- formatted_data = []
21
- for date, traffic in overview.items():
22
- formatted_data.append(f"Date: {date}, Organic Traffic: {traffic}")
23
 
24
- return "\n".join(formatted_data) if formatted_data else "No data found"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  except Exception as e:
27
- return f"Error: {str(e)}"
28
 
29
  def get_organic_summary(domain):
30
  """Get organic summary data from SemRush."""
31
  try:
 
 
 
32
  semrush = SemRush(SEMRUSH_USER_ID, SEMRUSH_API_KEY, domain)
33
  summary = semrush.organicSummary()
34
 
35
  if "error" in summary:
36
- return f"Error: {summary['error']}"
 
 
 
 
 
 
37
 
38
- # Format the data for display
39
- formatted_data = []
40
- for database, traffic in summary.items():
41
- formatted_data.append(f"Database: {database}, Organic Traffic: {traffic}")
42
 
43
- return "\n".join(formatted_data) if formatted_data else "No data found"
 
 
 
 
 
 
 
 
 
 
 
44
 
45
  except Exception as e:
46
- return f"Error: {str(e)}"
47
 
48
  def get_backlinks_summary(domain):
49
  """Get backlinks summary data from SemRush."""
50
  try:
 
 
 
51
  semrush = SemRush(SEMRUSH_USER_ID, SEMRUSH_API_KEY, domain)
52
  backlinks = semrush.backlinksSummary()
53
 
54
  if "error" in backlinks:
55
- return f"Error: {backlinks['error']}"
56
 
57
- # Format the data for display
58
- return json.dumps(backlinks, indent=2)
59
-
60
- except Exception as e:
61
- return f"Error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
- def get_all_data(domain):
64
- """Get all available data for a domain."""
65
- if not domain:
66
- return "Please enter a domain name"
67
-
68
- results = {}
69
-
70
- # Clean domain (remove protocol if present)
71
- clean_domain = domain.replace('https://', '').replace('http://', '').strip('/')
72
-
73
- # Get all data types
74
- results["Domain Overview"] = get_domain_overview(clean_domain)
75
- results["Organic Summary"] = get_organic_summary(clean_domain)
76
- results["Backlinks Summary"] = get_backlinks_summary(clean_domain)
77
-
78
- # Format output
79
- output = f"# SemRush Data for: {clean_domain}\n\n"
80
-
81
- for section, data in results.items():
82
- output += f"## {section}\n{data}\n\n"
83
 
84
- return output
 
85
 
86
- # Create Gradio interface
87
- with gr.Blocks(title="SemRush Domain Analyzer") as demo:
88
- gr.Markdown("# SemRush Domain Analyzer")
89
- gr.Markdown("Enter a domain name to get SEO data from SemRush API")
90
 
91
  with gr.Row():
92
  domain_input = gr.Textbox(
93
- label="Domain Name",
94
  placeholder="example.com or https://example.com",
95
- value=""
 
96
  )
 
97
 
98
- with gr.Row():
99
- analyze_btn = gr.Button("Analyze Domain", variant="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
- with gr.Row():
102
- output = gr.Textbox(
103
- label="Results",
104
- lines=20,
105
- max_lines=50,
106
- show_copy_button=True
107
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
  analyze_btn.click(
110
- fn=get_all_data,
111
  inputs=domain_input,
112
- outputs=output
113
  )
114
 
115
  # Add examples
116
  gr.Examples(
117
  examples=[
118
- ["example.com"],
119
- ["google.com"],
120
- ["github.com"]
 
121
  ],
122
  inputs=domain_input
123
  )
 
1
  import gradio as gr
2
  import os
 
3
  import json
4
+ import plotly.graph_objects as go
5
+ import plotly.express as px
6
+ import pandas as pd
7
+ from datetime import datetime
8
+ from parser import SemRush
9
 
10
  # Get credentials from Hugging Face Secrets
11
  SEMRUSH_USER_ID = os.getenv("SEMRUSH_USER_ID")
12
  SEMRUSH_API_KEY = os.getenv("SEMRUSH_API_KEY")
13
 
14
+ def format_number(num):
15
+ """Format large numbers with commas and abbreviations."""
16
+ if num >= 1000000000:
17
+ return f"{num/1000000000:.1f}B"
18
+ elif num >= 1000000:
19
+ return f"{num/1000000:.1f}M"
20
+ elif num >= 1000:
21
+ return f"{num/1000:.1f}K"
22
+ else:
23
+ return f"{num:,}"
24
+
25
+ def create_traffic_chart(data):
26
+ """Create a line chart for organic traffic over time."""
27
+ if not data or "error" in data:
28
+ return None
29
+
30
+ dates = []
31
+ traffic = []
32
+
33
+ for date_str, traffic_val in data.items():
34
+ # Parse date
35
+ try:
36
+ date_obj = datetime.strptime(date_str, "%Y%m%d")
37
+ dates.append(date_obj)
38
+ traffic.append(traffic_val)
39
+ except:
40
+ continue
41
+
42
+ if not dates:
43
+ return None
44
+
45
+ df = pd.DataFrame({'Date': dates, 'Traffic': traffic})
46
+ df = df.sort_values('Date')
47
+
48
+ fig = px.line(df, x='Date', y='Traffic',
49
+ title='Organic Traffic Trend Over Time',
50
+ labels={'Traffic': 'Organic Traffic', 'Date': 'Date'})
51
+
52
+ fig.update_traces(line=dict(color='#1f77b4', width=3))
53
+ fig.update_layout(
54
+ xaxis_title="Date",
55
+ yaxis_title="Organic Traffic",
56
+ hovermode='x unified',
57
+ template="plotly_white"
58
+ )
59
+
60
+ return fig
61
+
62
+ def create_organic_chart(data):
63
+ """Create a bar chart for top organic traffic by country."""
64
+ if not data or "error" in data:
65
+ return None
66
+
67
+ # Filter and sort data
68
+ filtered_data = {k: v for k, v in data.items() if v > 0}
69
+ sorted_data = dict(sorted(filtered_data.items(), key=lambda x: x[1], reverse=True)[:15])
70
+
71
+ if not sorted_data:
72
+ return None
73
+
74
+ countries = list(sorted_data.keys())
75
+ traffic = list(sorted_data.values())
76
+
77
+ fig = px.bar(x=traffic, y=countries, orientation='h',
78
+ title='Top 15 Countries by Organic Traffic',
79
+ labels={'x': 'Organic Traffic', 'y': 'Country/Database'})
80
+
81
+ fig.update_layout(
82
+ yaxis={'categoryorder': 'total ascending'},
83
+ template="plotly_white",
84
+ height=500
85
+ )
86
+
87
+ return fig
88
+
89
  def get_domain_overview(domain):
90
  """Get domain overview data from SemRush."""
91
  try:
92
+ if not SEMRUSH_USER_ID or not SEMRUSH_API_KEY:
93
+ return "❌ API credentials not found. Please set SEMRUSH_USER_ID and SEMRUSH_API_KEY in Secrets.", None
94
+
95
  semrush = SemRush(SEMRUSH_USER_ID, SEMRUSH_API_KEY, domain)
96
  overview = semrush.domainOverview()
97
 
98
  if "error" in overview:
99
+ return f"❌ Error: {overview['error']}", None
100
 
101
+ if not overview:
102
+ return "πŸ“Š No domain overview data found.", None
 
 
103
 
104
+ # Create chart
105
+ chart = create_traffic_chart(overview)
106
+
107
+ # Format text summary
108
+ formatted_lines = []
109
+ sorted_overview = dict(sorted(overview.items()))
110
+
111
+ for date_str, traffic in sorted_overview.items():
112
+ try:
113
+ date_obj = datetime.strptime(date_str, "%Y%m%d")
114
+ formatted_date = date_obj.strftime("%B %Y")
115
+ formatted_traffic = format_number(traffic)
116
+ formatted_lines.append(f"πŸ“… **{formatted_date}**: {formatted_traffic} visitors")
117
+ except:
118
+ formatted_lines.append(f"πŸ“… **{date_str}**: {format_number(traffic)} visitors")
119
+
120
+ result = "πŸ“ˆ **ORGANIC TRAFFIC TREND**\n\n" + "\n".join(formatted_lines)
121
+
122
+ return result, chart
123
 
124
  except Exception as e:
125
+ return f"❌ Error: {str(e)}", None
126
 
127
  def get_organic_summary(domain):
128
  """Get organic summary data from SemRush."""
129
  try:
130
+ if not SEMRUSH_USER_ID or not SEMRUSH_API_KEY:
131
+ return "❌ API credentials not found. Please set SEMRUSH_USER_ID and SEMRUSH_API_KEY in Secrets.", None
132
+
133
  semrush = SemRush(SEMRUSH_USER_ID, SEMRUSH_API_KEY, domain)
134
  summary = semrush.organicSummary()
135
 
136
  if "error" in summary:
137
+ return f"❌ Error: {summary['error']}", None
138
+
139
+ if not summary:
140
+ return "πŸ“Š No organic summary data found.", None
141
+
142
+ # Create chart
143
+ chart = create_organic_chart(summary)
144
 
145
+ # Format text summary - show top 20 countries
146
+ filtered_summary = {k: v for k, v in summary.items() if v > 0}
147
+ sorted_summary = dict(sorted(filtered_summary.items(), key=lambda x: x[1], reverse=True)[:20])
 
148
 
149
+ total_traffic = sum(sorted_summary.values())
150
+
151
+ formatted_lines = [f"🌍 **TOTAL ORGANIC TRAFFIC**: {format_number(total_traffic)} visitors\n"]
152
+
153
+ for i, (database, traffic) in enumerate(sorted_summary.items(), 1):
154
+ percentage = (traffic / total_traffic) * 100 if total_traffic > 0 else 0
155
+ flag_emoji = "πŸ†" if i <= 3 else "🌐"
156
+ formatted_lines.append(f"{flag_emoji} **{database.upper()}**: {format_number(traffic)} ({percentage:.1f}%)")
157
+
158
+ result = "\n".join(formatted_lines)
159
+
160
+ return result, chart
161
 
162
  except Exception as e:
163
+ return f"❌ Error: {str(e)}", None
164
 
165
  def get_backlinks_summary(domain):
166
  """Get backlinks summary data from SemRush."""
167
  try:
168
+ if not SEMRUSH_USER_ID or not SEMRUSH_API_KEY:
169
+ return "❌ API credentials not found. Please set SEMRUSH_USER_ID and SEMRUSH_API_KEY in Secrets."
170
+
171
  semrush = SemRush(SEMRUSH_USER_ID, SEMRUSH_API_KEY, domain)
172
  backlinks = semrush.backlinksSummary()
173
 
174
  if "error" in backlinks:
175
+ return f"❌ Error: {backlinks['error']}"
176
 
177
+ if not backlinks:
178
+ return "πŸ“Š No backlinks data found."
179
+
180
+ # Format backlinks data nicely
181
+ authority_score = backlinks.get('authorityScore', 0)
182
+ total_backlinks = backlinks.get('backlinks', 0)
183
+ health_score = backlinks.get('health', 0)
184
+ link_power = backlinks.get('linkPower', 0)
185
+ naturalness = backlinks.get('naturalness', 0)
186
+ referring_domains = backlinks.get('referringDomains', 0)
187
+ search_traffic = backlinks.get('searchTraffic', 0)
188
+
189
+ # Authority score interpretation
190
+ if authority_score >= 80:
191
+ authority_status = "🟒 Excellent"
192
+ elif authority_score >= 60:
193
+ authority_status = "🟑 Good"
194
+ elif authority_score >= 40:
195
+ authority_status = "🟠 Average"
196
+ else:
197
+ authority_status = "πŸ”΄ Poor"
198
+
199
+ # Health score interpretation
200
+ if health_score >= 80:
201
+ health_status = "🟒 Healthy"
202
+ elif health_score >= 60:
203
+ health_status = "🟑 Moderate"
204
+ elif health_score >= 40:
205
+ health_status = "🟠 Needs Attention"
206
+ else:
207
+ health_status = "πŸ”΄ Unhealthy"
208
+
209
+ result = f"""πŸ”— **BACKLINKS PROFILE SUMMARY**
210
 
211
+ πŸ† **Authority Score**: {authority_score}/100 ({authority_status})
212
+ πŸ“Š **Health Score**: {health_score}/100 ({health_status})
213
+
214
+ πŸ“ˆ **KEY METRICS**:
215
+ β€’ **Total Backlinks**: {format_number(total_backlinks)}
216
+ β€’ **Referring Domains**: {format_number(referring_domains)}
217
+ β€’ **Link Power**: {link_power}/10
218
+ β€’ **Naturalness**: {naturalness}/10
219
+ β€’ **Search Traffic**: {search_traffic}%
220
+
221
+ πŸ’‘ **INSIGHTS**:
222
+ β€’ Average of {total_backlinks/referring_domains:.1f} links per referring domain
223
+ β€’ {"Strong" if link_power >= 5 else "Moderate" if link_power >= 3 else "Weak"} link profile strength
224
+ β€’ {"Natural" if naturalness >= 7 else "Moderately natural" if naturalness >= 5 else "Potentially artificial"} link pattern"""
225
+
226
+ return result
 
 
 
 
227
 
228
+ except Exception as e:
229
+ return f"❌ Error: {str(e)}"
230
 
231
+ # Create Gradio interface with tabs
232
+ with gr.Blocks(title="SemRush Domain Analyzer", theme=gr.themes.Soft()) as demo:
233
+ gr.Markdown("# πŸ” SemRush Domain Analyzer")
234
+ gr.Markdown("**Comprehensive SEO analysis powered by SemRush API**")
235
 
236
  with gr.Row():
237
  domain_input = gr.Textbox(
238
+ label="🌐 Domain Name",
239
  placeholder="example.com or https://example.com",
240
+ value="",
241
+ scale=3
242
  )
243
+ analyze_btn = gr.Button("πŸš€ Analyze Domain", variant="primary", scale=1)
244
 
245
+ with gr.Tabs():
246
+ with gr.Tab("πŸ“ˆ Traffic Overview"):
247
+ with gr.Row():
248
+ with gr.Column(scale=1):
249
+ overview_output = gr.Markdown(
250
+ value="Enter a domain and click 'Analyze Domain' to see traffic trends.",
251
+ line_breaks=True
252
+ )
253
+ with gr.Column(scale=1):
254
+ overview_chart = gr.Plot(label="Traffic Trend Chart")
255
+
256
+ with gr.Tab("🌍 Geographic Distribution"):
257
+ with gr.Row():
258
+ with gr.Column(scale=1):
259
+ organic_output = gr.Markdown(
260
+ value="Geographic breakdown of organic traffic will appear here.",
261
+ line_breaks=True
262
+ )
263
+ with gr.Column(scale=1):
264
+ organic_chart = gr.Plot(label="Top Countries Chart")
265
+
266
+ with gr.Tab("πŸ”— Backlinks Profile"):
267
+ backlinks_output = gr.Markdown(
268
+ value="Comprehensive backlinks analysis will appear here.",
269
+ line_breaks=True
270
+ )
271
 
272
+ # Event handlers
273
+ def analyze_domain(domain):
274
+ if not domain:
275
+ return (
276
+ "⚠️ Please enter a domain name",
277
+ None,
278
+ "⚠️ Please enter a domain name",
279
+ None,
280
+ "⚠️ Please enter a domain name"
281
+ )
282
+
283
+ # Clean domain
284
+ clean_domain = domain.replace('https://', '').replace('http://', '').strip('/')
285
+
286
+ # Get all data
287
+ overview_text, overview_fig = get_domain_overview(clean_domain)
288
+ organic_text, organic_fig = get_organic_summary(clean_domain)
289
+ backlinks_text = get_backlinks_summary(clean_domain)
290
+
291
+ return overview_text, overview_fig, organic_text, organic_fig, backlinks_text
292
 
293
  analyze_btn.click(
294
+ fn=analyze_domain,
295
  inputs=domain_input,
296
+ outputs=[overview_output, overview_chart, organic_output, organic_chart, backlinks_output]
297
  )
298
 
299
  # Add examples
300
  gr.Examples(
301
  examples=[
302
+ ["google.com"],
303
+ ["github.com"],
304
+ ["stackoverflow.com"],
305
+ ["wikipedia.org"]
306
  ],
307
  inputs=domain_input
308
  )