13ze commited on
Commit
dd5deef
·
verified ·
1 Parent(s): cb892f1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +102 -65
app.py CHANGED
@@ -1,119 +1,156 @@
1
  import gradio as gr
2
  import html2text
3
  from bs4 import BeautifulSoup
 
4
 
5
- # --- Função de Limpeza de HTML ---
6
- def limpar_html(html_bruto):
 
 
7
  """
8
- Limpa o HTML, mantendo tags específicas e removendo outras,
9
- incluindo scripts, estilos e atributos desnecessários.
10
 
11
  :param html_bruto: String contendo o código HTML original.
12
- :return: String contendo o HTML limpo.
13
  """
14
  if not html_bruto:
15
  return ""
16
 
17
  soup = BeautifulSoup(html_bruto, 'html.parser')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
- # 1. Tags para remover completamente (incluindo conteúdo)
20
- tags_para_remover = ['script', 'style', 'header', 'footer', 'nav', 'aside', 'form', 'meta', 'link', 'noscript']
21
- for tag_nome in tags_para_remover:
22
- for tag in soup.find_all(tag_nome):
23
- tag.decompose() # Remove a tag e seu conteúdo
24
 
25
- # 2. Tags permitidas (vamos manter estas e seus conteúdos)
26
- # Todas as outras tags serão removidas, mas seu conteúdo será mantido (unwrap)
27
  tags_permitidas = {
28
- 'html', 'body', 'head', 'title', # Estrutura básica (head/title podem ser removidos pelo html2text depois)
29
- 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', # Cabeçalhos
30
- 'p', 'br', # Parágrafos e quebras de linha
31
- 'a', # Links
32
- 'strong', 'b', 'em', 'i', 'u', 's', 'strike', 'del', # Ênfase/Formatação
33
- 'ul', 'ol', 'li', # Listas
34
- 'img', # Imagens
35
- 'table', 'thead', 'tbody', 'tr', 'th', 'td', # Tabelas
36
- 'blockquote', # Citações
37
- 'pre', 'code' # Código
38
  }
39
-
40
- # 3. Atributos permitidos por tag (outros serão removidos)
41
  atributos_permitidos = {
42
  'a': ['href', 'title'],
43
- 'img': ['src', 'alt', 'title', 'width', 'height'], # Manter width/height pode ser útil
44
- '*': ['class', 'id'] # Permitir class e id em qualquer tag pode ser útil para CSS/JS, mas para conversão para Markdown talvez não. Remova se não precisar.
45
- # Adicione mais tags e seus atributos permitidos aqui se necessário
46
  }
47
 
48
- # Itera por todas as tags no documento
49
- for tag in soup.find_all(True): # True encontra todas as tags
 
 
 
 
 
50
  if tag.name not in tags_permitidas:
51
- # Se a tag não é permitida, remove a tag mas mantém o conteúdo
 
52
  tag.unwrap()
53
  else:
54
  # Se a tag é permitida, limpa os atributos não permitidos
55
- atributos_para_manter = atributos_permitidos.get(tag.name, []) + atributos_permitidos.get('*', [])
56
- # Cria um dicionário apenas com os atributos permitidos
57
  attrs_mantidos = {}
58
  for attr, value in tag.attrs.items():
59
  if attr in atributos_para_manter:
60
  attrs_mantidos[attr] = value
61
- # Define os atributos da tag para serem apenas os mantidos
62
  tag.attrs = attrs_mantidos
63
 
64
- # Retorna o HTML limpo como string
65
- # O pretty print pode ajudar na depuração, mas str(soup) é mais direto
66
- return str(soup)
67
 
68
  # --- Função Principal (adaptada) ---
69
- def html_para_markdown_com_limpeza(html_input):
70
  """
71
- Limpa o HTML e depois converte para Markdown.
72
-
73
- :param html_input: String contendo o código HTML vindo da interface Gradio.
74
- :return: String convertida para Markdown.
75
  """
76
  if not html_input:
77
  return "Por favor, insira algum código HTML."
78
 
79
  try:
80
- # 1. Limpa o HTML primeiro
81
- html_limpo = limpar_html(html_input)
82
 
83
- if not html_limpo:
84
- return "O HTML resultante após a limpeza está vazio."
85
 
86
  # 2. Converte o HTML limpo para Markdown
87
  converter = html2text.HTML2Text()
88
  converter.ignore_links = False
89
- # Configurações adicionais do html2text (opcional):
90
- converter.ignore_images = False # Garante que imagens sejam processadas
91
- converter.body_width = 0 # Evita quebra de linha automática baseada na largura
92
- # converter.skip_internal_links = True
93
- # converter.inline_links = True # Usa links inline em vez de referências no final
 
94
 
95
- markdown_output = converter.handle(html_limpo)
96
 
97
- # html2text pode incluir o conteúdo de <title> por padrão.
98
- # Se quiser remover especificamente o conteúdo do <title> do Markdown final:
99
- # (Isso é um pós-processamento, pode ser necessário ajustar)
100
- soup_limpo = BeautifulSoup(html_limpo, 'html.parser')
101
- titulo = soup_limpo.title
102
- if titulo and titulo.string:
103
- markdown_output = markdown_output.replace(titulo.string, '', 1).strip()
104
 
 
105
 
106
- return markdown_output
107
  except Exception as e:
108
- return f"Ocorreu um erro durante o processo: {str(e)}"
 
 
109
 
110
  # --- Cria a interface Gradio ---
111
  iface = gr.Interface(
112
- fn=html_para_markdown_com_limpeza, # Função principal atualizada
113
- inputs=gr.Textbox(lines=15, label="Insira o HTML bruto aqui", placeholder="<html><head><title>Título</title><script>...</script></head><body><h1>Cabeçalho</h1>...</body></html>"),
114
- outputs=gr.Textbox(lines=15, label="Markdown Resultante (após limpeza)"),
115
- title="Conversor HTML para Markdown com Limpeza",
116
- description="Cole seu código HTML na caixa da esquerda. O código será limpo (removendo scripts, estilos, tags e atributos desnecessários) e depois convertido para Markdown na caixa da direita. Tags como <h1>, <p>, <a> e <img> (com src/alt) são preservadas.",
117
  allow_flagging='never'
118
  )
119
 
 
1
  import gradio as gr
2
  import html2text
3
  from bs4 import BeautifulSoup
4
+ import logging # Para mensagens informativas
5
 
6
+ # Configurar logging básico para ver qual contêiner foi escolhido
7
+ logging.basicConfig(level=logging.INFO)
8
+
9
+ def extrair_limpar_html(html_bruto):
10
  """
11
+ Tenta extrair o conteúdo principal do HTML e depois o limpa,
12
+ removendo tags e atributos indesejados.
13
 
14
  :param html_bruto: String contendo o código HTML original.
15
+ :return: String contendo o HTML limpo e focado no conteúdo principal.
16
  """
17
  if not html_bruto:
18
  return ""
19
 
20
  soup = BeautifulSoup(html_bruto, 'html.parser')
21
+ target_element = None
22
+
23
+ # --- 1. Tentar Extrair o Conteúdo Principal ---
24
+ # Lista de seletores CSS em ordem de preferência
25
+ # (Mais específicos primeiro)
26
+ main_content_selectors = [
27
+ 'main', # <main> tag
28
+ 'article', # <article> tag (primeiro encontrado)
29
+ '[role="main"]', # role="main" attribute
30
+ '#content', # ID #content
31
+ '#main', # ID #main
32
+ '.main-content', # Classe .main-content
33
+ '.post', # Classe .post
34
+ '.entry-content', # Classe .entry-content (comum em blogs)
35
+ '.post-body', # Classe .post-body
36
+ '.article-body', # Classe .article-body
37
+ '#bodyContent', # ID específico da Wikipedia
38
+ # Adicione outros seletores comuns se necessário
39
+ ]
40
+
41
+ for selector in main_content_selectors:
42
+ # select_one retorna o primeiro elemento que corresponde ao seletor
43
+ potential_match = soup.select_one(selector)
44
+ if potential_match:
45
+ target_element = potential_match
46
+ logging.info(f"Conteúdo principal identificado usando o seletor: '{selector}'")
47
+ break # Para ao encontrar o primeiro match da lista
48
+
49
+ # Fallback: Se nenhum seletor específico funcionou, usar o <body>
50
+ if not target_element:
51
+ if soup.body:
52
+ target_element = soup.body
53
+ logging.info("Nenhum contêiner principal específico encontrado. Usando <body> como fallback.")
54
+ else:
55
+ # Caso extremo: HTML sem body, usa o soup inteiro
56
+ target_element = soup
57
+ logging.warning("Nenhum contêiner principal ou <body> encontrado. Processando todo o input.")
58
+
59
+ # Se target_element for None (caso extremo), retorna vazio
60
+ if not target_element:
61
+ return ""
62
+
63
+ # --- 2. Limpar o Elemento Selecionado ---
64
+ # Agora aplicamos a limpeza *dentro* do target_element
65
 
66
+ # Tags para remover completamente (script, style, etc.)
67
+ tags_para_remover = ['script', 'style', 'header', 'footer', 'nav', 'aside', 'form', 'meta', 'link', 'noscript', 'button', 'input', 'select', 'textarea', 'label']
68
+ for tag in target_element.find_all(tags_para_remover):
69
+ tag.decompose()
 
70
 
71
+ # Tags permitidas e atributos permitidos (ajuste conforme necessário)
 
72
  tags_permitidas = {
73
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'br', 'a', 'strong', 'b',
74
+ 'em', 'i', 'u', 's', 'strike', 'del', 'ul', 'ol', 'li', 'img',
75
+ 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'blockquote', 'pre', 'code'
76
+ # Removi html, head, body, title daqui, pois estamos focando no conteúdo
 
 
 
 
 
 
77
  }
 
 
78
  atributos_permitidos = {
79
  'a': ['href', 'title'],
80
+ 'img': ['src', 'alt', 'title', 'width', 'height'],
81
+ # Remover '*' para ser mais restritivo? Ou manter class/id? Por enquanto, removido.
82
+ # '*': ['class', 'id']
83
  }
84
 
85
+ # Iterar por todas as tags DENTRO do target_element
86
+ # Usar list() para poder modificar a estrutura durante a iteração
87
+ for tag in list(target_element.find_all(True)):
88
+ # Verifica se a tag ainda existe (pode ter sido removida por decompose)
89
+ if not tag.parent:
90
+ continue
91
+
92
  if tag.name not in tags_permitidas:
93
+ # Se a tag não é permitida, remove a tag mas mantém o conteúdo (unwrap)
94
+ # Evitar erro se tag já foi removida
95
  tag.unwrap()
96
  else:
97
  # Se a tag é permitida, limpa os atributos não permitidos
98
+ atributos_para_manter = atributos_permitidos.get(tag.name, []) # + atributos_permitidos.get('*', []) # Removido '*'
 
99
  attrs_mantidos = {}
100
  for attr, value in tag.attrs.items():
101
  if attr in atributos_para_manter:
102
  attrs_mantidos[attr] = value
 
103
  tag.attrs = attrs_mantidos
104
 
105
+ # Retorna o HTML limpo e focado como string
106
+ return str(target_element)
 
107
 
108
  # --- Função Principal (adaptada) ---
109
+ def html_para_markdown_com_extracao_e_limpeza(html_input):
110
  """
111
+ Extrai o conteúdo principal do HTML, limpa-o e converte para Markdown.
 
 
 
112
  """
113
  if not html_input:
114
  return "Por favor, insira algum código HTML."
115
 
116
  try:
117
+ # 1. Extrai o conteúdo principal e limpa
118
+ html_limpo_e_focado = extrair_limpar_html(html_input)
119
 
120
+ if not html_limpo_e_focado or html_limpo_e_focado.strip() == "":
121
+ return "HTML resultante após extração e limpeza está vazio ou contém apenas espaços."
122
 
123
  # 2. Converte o HTML limpo para Markdown
124
  converter = html2text.HTML2Text()
125
  converter.ignore_links = False
126
+ converter.ignore_images = False
127
+ converter.body_width = 0 # Desativa quebra de linha baseada em largura
128
+ # Outras opções úteis do html2text:
129
+ # converter.protect_links = True # Tenta proteger links que podem ser quebrados
130
+ # converter.single_line_break = True # Usa quebra de linha única para <br>
131
+ # converter.use_automatic_links = True # Detecta links automaticamente
132
 
133
+ markdown_output = converter.handle(html_limpo_e_focado)
134
 
135
+ # Pequeno pós-processamento para remover linhas vazias excessivas
136
+ linhas = markdown_output.splitlines()
137
+ linhas_filtradas = [linha for linha in linhas if linha.strip()]
138
+ markdown_output = "\n\n".join(linhas_filtradas) # Junta com parágrafos separados
 
 
 
139
 
140
+ return markdown_output.strip()
141
 
 
142
  except Exception as e:
143
+ # Logar o erro completo pode ser útil para depuração no servidor
144
+ logging.error(f"Erro durante o processo: {e}", exc_info=True)
145
+ return f"Ocorreu um erro durante o processo: {str(e)}. Verifique os logs para mais detalhes."
146
 
147
  # --- Cria a interface Gradio ---
148
  iface = gr.Interface(
149
+ fn=html_para_markdown_com_extracao_e_limpeza, # Função principal atualizada
150
+ inputs=gr.Textbox(lines=20, label="Insira o HTML bruto aqui", placeholder="Cole o código-fonte HTML completo da página..."),
151
+ outputs=gr.Textbox(lines=20, label="Markdown Resultante (Conteúdo Principal)"),
152
+ title="Conversor HTML para Markdown (Foco no Conteúdo Principal)",
153
+ description="Cole o HTML completo. O script tentará identificar o conteúdo principal (usando tags como <main>, <article> ou IDs/classes comuns como #content, .post-body), removerá elementos irrelevantes (scripts, navegação, rodapés, etc.) e atributos desnecessários, e então converterá o conteúdo limpo para Markdown.",
154
  allow_flagging='never'
155
  )
156