Mayrasilva commited on
Commit
e9dfeb6
·
verified ·
1 Parent(s): b424aa5

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +208 -0
app.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import gradio as gr
3
+ import requests
4
+ from bs4 import BeautifulSoup
5
+ import json
6
+ from datetime import datetime
7
+
8
+ def calculate_trust_score(url):
9
+ try:
10
+ if not url.startswith(('http://', 'https://')):
11
+ url = 'https://' + url
12
+
13
+ headers = {
14
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
15
+ }
16
+ response = requests.get(url, headers=headers, timeout=10)
17
+ soup = BeautifulSoup(response.text, 'html.parser')
18
+
19
+ trust_score = 0
20
+ details = {
21
+ 'identity': {'score': 0, 'max': 40, 'items': []},
22
+ 'content': {'score': 0, 'max': 40, 'items': []},
23
+ 'signals': {'score': 0, 'max': 20, 'items': []}
24
+ }
25
+
26
+ scripts = soup.find_all('script', type='application/ld+json')
27
+ has_organization = False
28
+ has_person = False
29
+ has_same_as = False
30
+
31
+ for script in scripts:
32
+ try:
33
+ data = json.loads(script.string)
34
+ data_items = data if isinstance(data, list) else [data]
35
+
36
+ for item in data_items:
37
+ if "@type" in item:
38
+ if item["@type"] == "Organization":
39
+ has_organization = True
40
+ details['identity']['score'] += 15
41
+ details['identity']['items'].append("✅ Organização identificada")
42
+
43
+ if "logo" in item:
44
+ details['identity']['score'] += 5
45
+ details['identity']['items'].append("✅ Logo definida")
46
+
47
+ if "sameAs" in item:
48
+ has_same_as = True
49
+ details['identity']['score'] += 10
50
+ details['identity']['items'].append("✅ Links de verificação (sameAs)")
51
+
52
+ elif item["@type"] == "Person":
53
+ has_person = True
54
+ details['identity']['score'] += 10
55
+ details['identity']['items'].append("✅ Pessoa identificada")
56
+
57
+ if "sameAs" in item:
58
+ has_same_as = True
59
+ details['identity']['score'] += 10
60
+ details['identity']['items'].append("✅ Links sociais da pessoa")
61
+
62
+ elif item["@type"] == "Article":
63
+ details['content']['score'] += 15
64
+ details['content']['items'].append("✅ Artigo estruturado")
65
+
66
+ if "author" in item:
67
+ details['content']['score'] += 10
68
+ details['content']['items'].append("✅ Autor identificado")
69
+
70
+ if "datePublished" in item:
71
+ details['content']['score'] += 5
72
+ details['content']['items'].append("✅ Data de publicação")
73
+ except:
74
+ continue
75
+
76
+ h1_count = len(soup.find_all('h1'))
77
+ h2_count = len(soup.find_all('h2'))
78
+
79
+ if h1_count == 1:
80
+ details['content']['score'] += 5
81
+ details['content']['items'].append("✅ Estrutura H1 única")
82
+ elif h1_count > 1:
83
+ details['content']['items'].append("⚠️ Múltiplos H1 encontrados")
84
+
85
+ if h2_count > 0:
86
+ details['content']['score'] += 5
87
+ details['content']['items'].append("✅ Estrutura H2 presente")
88
+
89
+ title = soup.find('title')
90
+ meta_desc = soup.find('meta', attrs={'name': 'description'})
91
+
92
+ if title and len(title.get_text().strip()) > 10:
93
+ details['content']['score'] += 5
94
+ details['content']['items'].append("✅ Título adequado")
95
+
96
+ if meta_desc and len(meta_desc.get('content', '').strip()) > 50:
97
+ details['content']['score'] += 5
98
+ details['content']['items'].append("✅ Meta description presente")
99
+
100
+ if url.startswith('https://'):
101
+ details['signals']['score'] += 10
102
+ details['signals']['items'].append("✅ HTTPS ativo")
103
+
104
+ contact_indicators = ['contact', 'email', 'phone', 'address', 'sobre', 'about']
105
+ page_text = soup.get_text().lower()
106
+
107
+ if any(indicator in page_text for indicator in contact_indicators):
108
+ details['signals']['score'] += 5
109
+ details['signals']['items'].append("✅ Informações de contato")
110
+
111
+ external_links = soup.find_all('a', href=True)
112
+ quality_domains = ['github.com', 'linkedin.com', 'medium.com', 'wikidata.org', 'keybase.io']
113
+
114
+ for link in external_links:
115
+ href = link.get('href', '')
116
+ if any(domain in href for domain in quality_domains):
117
+ details['signals']['score'] += 5
118
+ details['signals']['items'].append("✅ Links para plataformas confiáveis")
119
+ break
120
+
121
+ total_score = min(
122
+ details['identity']['score'] +
123
+ details['content']['score'] +
124
+ details['signals']['score'],
125
+ 100
126
+ )
127
+
128
+ if total_score >= 80:
129
+ level = "🚀 Excelente"
130
+ level_color = "🟢"
131
+ elif total_score >= 60:
132
+ level = "👍 Bom"
133
+ level_color = "🟡"
134
+ elif total_score >= 40:
135
+ level = "⚠️ Precisa melhorar"
136
+ level_color = "🟠"
137
+ else:
138
+ level = "❌ Crítico"
139
+ level_color = "🔴"
140
+
141
+ suggestions = []
142
+
143
+ if not has_organization and not has_person:
144
+ suggestions.append("🎯 **Prioridade Alta**: Adicione schema.org com @type Organization ou Person")
145
+
146
+ if not has_same_as:
147
+ suggestions.append("🔗 Adicione links 'sameAs' para LinkedIn, GitHub ou outras plataformas")
148
+
149
+ if details['content']['score'] < 20:
150
+ suggestions.append("📝 Melhore a estrutura do conteúdo com headings H1/H2 e meta tags")
151
+
152
+ if details['signals']['score'] < 10:
153
+ suggestions.append("🔒 Garanta HTTPS e adicione informações de contato")
154
+
155
+ if total_score >= 80:
156
+ suggestions.append("🎉 Parabéns! Seu site está bem otimizado para LLMs")
157
+
158
+ report = f"""# {level_color} **Trust Score: {total_score}/100**
159
+
160
+ ## {level}
161
+
162
+ ### 📊 **Detalhamento por Categoria**
163
+
164
+ #### 🏢 **Identidade Digital** ({details['identity']['score']}/{details['identity']['max']} pontos)
165
+
166
+ {chr(10).join(details['identity']['items']) if details['identity']['items'] else "❌ Nenhuma identidade estruturada encontrada"}
167
+
168
+ #### 📝 **Conteúdo Estruturado** ({details['content']['score']}/{details['content']['max']} pontos)
169
+
170
+ {chr(10).join(details['content']['items']) if details['content']['items'] else "❌ Estrutura de conteúdo não otimizada"}
171
+
172
+ #### 🔒 **Sinais de Confiança** ({details['signals']['score']}/{details['signals']['max']} pontos)
173
+
174
+ {chr(10).join(details['signals']['items']) if details['signals']['items'] else "❌ Sinais de confiança ausentes"}
175
+
176
+ ### 💡 **Próximos Passos**
177
+
178
+ {chr(10).join(f"• {tip}" for tip in suggestions)}
179
+
180
+ ### 📚 **Recursos Úteis**
181
+
182
+ • [Schema.org Generator](https://schema.org/)
183
+ • [Google Rich Results Test](https://search.google.com/test/rich-results)
184
+ • [Structured Data Markup Helper](https://www.google.com/webmasters/markup-helper/)
185
+
186
+ -----
187
+
188
+ *💡 Dica: Um site bem estruturado não só melhora a visibilidade para LLMs, mas também para mecanismos de busca!*
189
+ """
190
+ return report
191
+
192
+ except requests.exceptions.RequestException as e:
193
+ return f"❌ **Erro de conexão**: Não foi possível acessar o site.\n\n*Verifique se a URL está correta e o site está online.*"
194
+ except Exception as e:
195
+ return f"❌ **Erro inesperado**: {str(e)}\n\n*Tente novamente ou verifique se a URL está no formato correto.*"
196
+
197
+ import gradio as gr
198
+
199
+ with gr.Blocks(title="🤖 LLM Trust Score") as demo:
200
+ gr.Markdown("## 🤖 LLM Trust Score Calculator\nDescubra como seu site é percebido por modelos como ChatGPT, Claude, Perplexity e Grok.")
201
+
202
+ url_input = gr.Textbox(label="🌐 URL do seu site", placeholder="exemplo.com ou https://seusite.com.br")
203
+ output = gr.Markdown()
204
+
205
+ analyze_btn = gr.Button("🔍 Analisar Site")
206
+ analyze_btn.click(fn=calculate_trust_score, inputs=url_input, outputs=output)
207
+
208
+ demo.launch()